Commit ae19161e authored by Jeff Garzik's avatar Jeff Garzik Committed by Jeff Garzik

Merge branch 'for-2.6.28' of git://git.marvell.com/mv643xx_eth into upstream-next

parents 152cbcf9 042af53c
...@@ -25,7 +25,7 @@ ...@@ -25,7 +25,7 @@
#include "common.h" #include "common.h"
static struct mv643xx_eth_platform_data db88f6281_ge00_data = { static struct mv643xx_eth_platform_data db88f6281_ge00_data = {
.phy_addr = 8, .phy_addr = MV643XX_ETH_PHY_ADDR(8),
}; };
static struct mv_sata_platform_data db88f6281_sata_data = { static struct mv_sata_platform_data db88f6281_sata_data = {
......
...@@ -30,7 +30,7 @@ ...@@ -30,7 +30,7 @@
#define RD88F6192_GPIO_USB_VBUS 10 #define RD88F6192_GPIO_USB_VBUS 10
static struct mv643xx_eth_platform_data rd88f6192_ge00_data = { static struct mv643xx_eth_platform_data rd88f6192_ge00_data = {
.phy_addr = 8, .phy_addr = MV643XX_ETH_PHY_ADDR(8),
}; };
static struct mv_sata_platform_data rd88f6192_sata_data = { static struct mv_sata_platform_data rd88f6192_sata_data = {
......
...@@ -69,7 +69,7 @@ static struct platform_device rd88f6281_nand_flash = { ...@@ -69,7 +69,7 @@ static struct platform_device rd88f6281_nand_flash = {
}; };
static struct mv643xx_eth_platform_data rd88f6281_ge00_data = { static struct mv643xx_eth_platform_data rd88f6281_ge00_data = {
.phy_addr = -1, .phy_addr = MV643XX_ETH_PHY_NONE,
.speed = SPEED_1000, .speed = SPEED_1000,
.duplex = DUPLEX_FULL, .duplex = DUPLEX_FULL,
}; };
......
...@@ -67,7 +67,7 @@ static struct platform_device lb88rc8480_boot_flash = { ...@@ -67,7 +67,7 @@ static struct platform_device lb88rc8480_boot_flash = {
}; };
static struct mv643xx_eth_platform_data lb88rc8480_ge0_data = { static struct mv643xx_eth_platform_data lb88rc8480_ge0_data = {
.phy_addr = 1, .phy_addr = MV643XX_ETH_PHY_ADDR(1),
.mac_addr = { 0x00, 0x50, 0x43, 0x11, 0x22, 0x33 }, .mac_addr = { 0x00, 0x50, 0x43, 0x11, 0x22, 0x33 },
}; };
......
...@@ -330,6 +330,7 @@ void __init mv78xx0_ge00_init(struct mv643xx_eth_platform_data *eth_data) ...@@ -330,6 +330,7 @@ void __init mv78xx0_ge00_init(struct mv643xx_eth_platform_data *eth_data)
struct mv643xx_eth_shared_platform_data mv78xx0_ge01_shared_data = { struct mv643xx_eth_shared_platform_data mv78xx0_ge01_shared_data = {
.t_clk = 0, .t_clk = 0,
.dram = &mv78xx0_mbus_dram_info, .dram = &mv78xx0_mbus_dram_info,
.shared_smi = &mv78xx0_ge00_shared,
}; };
static struct resource mv78xx0_ge01_shared_resources[] = { static struct resource mv78xx0_ge01_shared_resources[] = {
...@@ -370,7 +371,6 @@ static struct platform_device mv78xx0_ge01 = { ...@@ -370,7 +371,6 @@ static struct platform_device mv78xx0_ge01 = {
void __init mv78xx0_ge01_init(struct mv643xx_eth_platform_data *eth_data) void __init mv78xx0_ge01_init(struct mv643xx_eth_platform_data *eth_data)
{ {
eth_data->shared = &mv78xx0_ge01_shared; eth_data->shared = &mv78xx0_ge01_shared;
eth_data->shared_smi = &mv78xx0_ge00_shared;
mv78xx0_ge01.dev.platform_data = eth_data; mv78xx0_ge01.dev.platform_data = eth_data;
platform_device_register(&mv78xx0_ge01_shared); platform_device_register(&mv78xx0_ge01_shared);
...@@ -384,6 +384,7 @@ void __init mv78xx0_ge01_init(struct mv643xx_eth_platform_data *eth_data) ...@@ -384,6 +384,7 @@ void __init mv78xx0_ge01_init(struct mv643xx_eth_platform_data *eth_data)
struct mv643xx_eth_shared_platform_data mv78xx0_ge10_shared_data = { struct mv643xx_eth_shared_platform_data mv78xx0_ge10_shared_data = {
.t_clk = 0, .t_clk = 0,
.dram = &mv78xx0_mbus_dram_info, .dram = &mv78xx0_mbus_dram_info,
.shared_smi = &mv78xx0_ge00_shared,
}; };
static struct resource mv78xx0_ge10_shared_resources[] = { static struct resource mv78xx0_ge10_shared_resources[] = {
...@@ -424,7 +425,6 @@ static struct platform_device mv78xx0_ge10 = { ...@@ -424,7 +425,6 @@ static struct platform_device mv78xx0_ge10 = {
void __init mv78xx0_ge10_init(struct mv643xx_eth_platform_data *eth_data) void __init mv78xx0_ge10_init(struct mv643xx_eth_platform_data *eth_data)
{ {
eth_data->shared = &mv78xx0_ge10_shared; eth_data->shared = &mv78xx0_ge10_shared;
eth_data->shared_smi = &mv78xx0_ge00_shared;
mv78xx0_ge10.dev.platform_data = eth_data; mv78xx0_ge10.dev.platform_data = eth_data;
platform_device_register(&mv78xx0_ge10_shared); platform_device_register(&mv78xx0_ge10_shared);
...@@ -438,6 +438,7 @@ void __init mv78xx0_ge10_init(struct mv643xx_eth_platform_data *eth_data) ...@@ -438,6 +438,7 @@ void __init mv78xx0_ge10_init(struct mv643xx_eth_platform_data *eth_data)
struct mv643xx_eth_shared_platform_data mv78xx0_ge11_shared_data = { struct mv643xx_eth_shared_platform_data mv78xx0_ge11_shared_data = {
.t_clk = 0, .t_clk = 0,
.dram = &mv78xx0_mbus_dram_info, .dram = &mv78xx0_mbus_dram_info,
.shared_smi = &mv78xx0_ge00_shared,
}; };
static struct resource mv78xx0_ge11_shared_resources[] = { static struct resource mv78xx0_ge11_shared_resources[] = {
...@@ -478,7 +479,6 @@ static struct platform_device mv78xx0_ge11 = { ...@@ -478,7 +479,6 @@ static struct platform_device mv78xx0_ge11 = {
void __init mv78xx0_ge11_init(struct mv643xx_eth_platform_data *eth_data) void __init mv78xx0_ge11_init(struct mv643xx_eth_platform_data *eth_data)
{ {
eth_data->shared = &mv78xx0_ge11_shared; eth_data->shared = &mv78xx0_ge11_shared;
eth_data->shared_smi = &mv78xx0_ge00_shared;
mv78xx0_ge11.dev.platform_data = eth_data; mv78xx0_ge11.dev.platform_data = eth_data;
platform_device_register(&mv78xx0_ge11_shared); platform_device_register(&mv78xx0_ge11_shared);
......
...@@ -19,19 +19,19 @@ ...@@ -19,19 +19,19 @@
#include "common.h" #include "common.h"
static struct mv643xx_eth_platform_data db78x00_ge00_data = { static struct mv643xx_eth_platform_data db78x00_ge00_data = {
.phy_addr = 8, .phy_addr = MV643XX_ETH_PHY_ADDR(8),
}; };
static struct mv643xx_eth_platform_data db78x00_ge01_data = { static struct mv643xx_eth_platform_data db78x00_ge01_data = {
.phy_addr = 9, .phy_addr = MV643XX_ETH_PHY_ADDR(9),
}; };
static struct mv643xx_eth_platform_data db78x00_ge10_data = { static struct mv643xx_eth_platform_data db78x00_ge10_data = {
.phy_addr = -1, .phy_addr = MV643XX_ETH_PHY_NONE,
}; };
static struct mv643xx_eth_platform_data db78x00_ge11_data = { static struct mv643xx_eth_platform_data db78x00_ge11_data = {
.phy_addr = -1, .phy_addr = MV643XX_ETH_PHY_NONE,
}; };
static struct mv_sata_platform_data db78x00_sata_data = { static struct mv_sata_platform_data db78x00_sata_data = {
......
...@@ -285,7 +285,7 @@ subsys_initcall(db88f5281_pci_init); ...@@ -285,7 +285,7 @@ subsys_initcall(db88f5281_pci_init);
* Ethernet * Ethernet
****************************************************************************/ ****************************************************************************/
static struct mv643xx_eth_platform_data db88f5281_eth_data = { static struct mv643xx_eth_platform_data db88f5281_eth_data = {
.phy_addr = 8, .phy_addr = MV643XX_ETH_PHY_ADDR(8),
}; };
/***************************************************************************** /*****************************************************************************
......
...@@ -79,7 +79,7 @@ subsys_initcall(dns323_pci_init); ...@@ -79,7 +79,7 @@ subsys_initcall(dns323_pci_init);
*/ */
static struct mv643xx_eth_platform_data dns323_eth_data = { static struct mv643xx_eth_platform_data dns323_eth_data = {
.phy_addr = 8, .phy_addr = MV643XX_ETH_PHY_ADDR(8),
}; };
/**************************************************************************** /****************************************************************************
......
...@@ -161,7 +161,7 @@ subsys_initcall(kurobox_pro_pci_init); ...@@ -161,7 +161,7 @@ subsys_initcall(kurobox_pro_pci_init);
****************************************************************************/ ****************************************************************************/
static struct mv643xx_eth_platform_data kurobox_pro_eth_data = { static struct mv643xx_eth_platform_data kurobox_pro_eth_data = {
.phy_addr = 8, .phy_addr = MV643XX_ETH_PHY_ADDR(8),
}; };
/***************************************************************************** /*****************************************************************************
......
...@@ -109,7 +109,7 @@ subsys_initcall(mss2_pci_init); ...@@ -109,7 +109,7 @@ subsys_initcall(mss2_pci_init);
****************************************************************************/ ****************************************************************************/
static struct mv643xx_eth_platform_data mss2_eth_data = { static struct mv643xx_eth_platform_data mss2_eth_data = {
.phy_addr = 8, .phy_addr = MV643XX_ETH_PHY_ADDR(8),
}; };
/***************************************************************************** /*****************************************************************************
......
...@@ -39,7 +39,7 @@ ...@@ -39,7 +39,7 @@
* Ethernet * Ethernet
****************************************************************************/ ****************************************************************************/
static struct mv643xx_eth_platform_data mv2120_eth_data = { static struct mv643xx_eth_platform_data mv2120_eth_data = {
.phy_addr = 8, .phy_addr = MV643XX_ETH_PHY_ADDR(8),
}; };
static struct mv_sata_platform_data mv2120_sata_data = { static struct mv_sata_platform_data mv2120_sata_data = {
......
...@@ -88,7 +88,7 @@ static struct orion5x_mpp_mode rd88f5181l_fxo_mpp_modes[] __initdata = { ...@@ -88,7 +88,7 @@ static struct orion5x_mpp_mode rd88f5181l_fxo_mpp_modes[] __initdata = {
}; };
static struct mv643xx_eth_platform_data rd88f5181l_fxo_eth_data = { static struct mv643xx_eth_platform_data rd88f5181l_fxo_eth_data = {
.phy_addr = -1, .phy_addr = MV643XX_ETH_PHY_NONE,
.speed = SPEED_1000, .speed = SPEED_1000,
.duplex = DUPLEX_FULL, .duplex = DUPLEX_FULL,
}; };
......
...@@ -89,7 +89,7 @@ static struct orion5x_mpp_mode rd88f5181l_ge_mpp_modes[] __initdata = { ...@@ -89,7 +89,7 @@ static struct orion5x_mpp_mode rd88f5181l_ge_mpp_modes[] __initdata = {
}; };
static struct mv643xx_eth_platform_data rd88f5181l_ge_eth_data = { static struct mv643xx_eth_platform_data rd88f5181l_ge_eth_data = {
.phy_addr = -1, .phy_addr = MV643XX_ETH_PHY_NONE,
.speed = SPEED_1000, .speed = SPEED_1000,
.duplex = DUPLEX_FULL, .duplex = DUPLEX_FULL,
}; };
......
...@@ -221,7 +221,7 @@ subsys_initcall(rd88f5182_pci_init); ...@@ -221,7 +221,7 @@ subsys_initcall(rd88f5182_pci_init);
****************************************************************************/ ****************************************************************************/
static struct mv643xx_eth_platform_data rd88f5182_eth_data = { static struct mv643xx_eth_platform_data rd88f5182_eth_data = {
.phy_addr = 8, .phy_addr = MV643XX_ETH_PHY_ADDR(8),
}; };
/***************************************************************************** /*****************************************************************************
......
...@@ -103,8 +103,7 @@ static struct platform_device ts78xx_nor_boot_flash = { ...@@ -103,8 +103,7 @@ static struct platform_device ts78xx_nor_boot_flash = {
* Ethernet * Ethernet
****************************************************************************/ ****************************************************************************/
static struct mv643xx_eth_platform_data ts78xx_eth_data = { static struct mv643xx_eth_platform_data ts78xx_eth_data = {
.phy_addr = 0, .phy_addr = MV643XX_ETH_PHY_ADDR(0),
.force_phy_addr = 1,
}; };
/***************************************************************************** /*****************************************************************************
......
...@@ -48,7 +48,7 @@ void qnap_tsx09_power_off(void) ...@@ -48,7 +48,7 @@ void qnap_tsx09_power_off(void)
****************************************************************************/ ****************************************************************************/
struct mv643xx_eth_platform_data qnap_tsx09_eth_data = { struct mv643xx_eth_platform_data qnap_tsx09_eth_data = {
.phy_addr = 8, .phy_addr = MV643XX_ETH_PHY_ADDR(8),
}; };
static int __init qnap_tsx09_parse_hex_nibble(char n) static int __init qnap_tsx09_parse_hex_nibble(char n)
......
...@@ -92,7 +92,7 @@ static struct platform_device wnr854t_nor_flash = { ...@@ -92,7 +92,7 @@ static struct platform_device wnr854t_nor_flash = {
}; };
static struct mv643xx_eth_platform_data wnr854t_eth_data = { static struct mv643xx_eth_platform_data wnr854t_eth_data = {
.phy_addr = -1, .phy_addr = MV643XX_ETH_PHY_NONE,
.speed = SPEED_1000, .speed = SPEED_1000,
.duplex = DUPLEX_FULL, .duplex = DUPLEX_FULL,
}; };
......
...@@ -100,7 +100,7 @@ static struct platform_device wrt350n_v2_nor_flash = { ...@@ -100,7 +100,7 @@ static struct platform_device wrt350n_v2_nor_flash = {
}; };
static struct mv643xx_eth_platform_data wrt350n_v2_eth_data = { static struct mv643xx_eth_platform_data wrt350n_v2_eth_data = {
.phy_addr = -1, .phy_addr = MV643XX_ETH_PHY_NONE,
.speed = SPEED_1000, .speed = SPEED_1000,
.duplex = DUPLEX_FULL, .duplex = DUPLEX_FULL,
}; };
......
...@@ -293,10 +293,8 @@ static int __init mv64x60_eth_device_setup(struct device_node *np, int id, ...@@ -293,10 +293,8 @@ static int __init mv64x60_eth_device_setup(struct device_node *np, int id,
return -ENODEV; return -ENODEV;
prop = of_get_property(phy, "reg", NULL); prop = of_get_property(phy, "reg", NULL);
if (prop) { if (prop)
pdata.force_phy_addr = 1; pdata.phy_addr = MV643XX_ETH_PHY_ADDR(*prop);
pdata.phy_addr = *prop;
}
of_node_put(phy); of_node_put(phy);
......
...@@ -2274,7 +2274,7 @@ config UGETH_TX_ON_DEMAND ...@@ -2274,7 +2274,7 @@ config UGETH_TX_ON_DEMAND
config MV643XX_ETH config MV643XX_ETH
tristate "Marvell Discovery (643XX) and Orion ethernet support" tristate "Marvell Discovery (643XX) and Orion ethernet support"
depends on MV64360 || MV64X60 || (PPC_MULTIPLATFORM && PPC32) || PLAT_ORION depends on MV64360 || MV64X60 || (PPC_MULTIPLATFORM && PPC32) || PLAT_ORION
select MII select PHYLIB
help help
This driver supports the gigabit ethernet MACs in the This driver supports the gigabit ethernet MACs in the
Marvell Discovery PPC/MIPS chipset family (MV643XX) and Marvell Discovery PPC/MIPS chipset family (MV643XX) and
......
...@@ -48,30 +48,28 @@ ...@@ -48,30 +48,28 @@
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/spinlock.h> #include <linux/spinlock.h>
#include <linux/workqueue.h> #include <linux/workqueue.h>
#include <linux/mii.h> #include <linux/phy.h>
#include <linux/mv643xx_eth.h> #include <linux/mv643xx_eth.h>
#include <asm/io.h> #include <asm/io.h>
#include <asm/types.h> #include <asm/types.h>
#include <asm/system.h> #include <asm/system.h>
static char mv643xx_eth_driver_name[] = "mv643xx_eth"; static char mv643xx_eth_driver_name[] = "mv643xx_eth";
static char mv643xx_eth_driver_version[] = "1.3"; static char mv643xx_eth_driver_version[] = "1.4";
#define MV643XX_ETH_CHECKSUM_OFFLOAD_TX
#define MV643XX_ETH_NAPI
#define MV643XX_ETH_TX_FAST_REFILL
#ifdef MV643XX_ETH_CHECKSUM_OFFLOAD_TX
#define MAX_DESCS_PER_SKB (MAX_SKB_FRAGS + 1)
#else
#define MAX_DESCS_PER_SKB 1
#endif
/* /*
* Registers shared between all ports. * Registers shared between all ports.
*/ */
#define PHY_ADDR 0x0000 #define PHY_ADDR 0x0000
#define SMI_REG 0x0004 #define SMI_REG 0x0004
#define SMI_BUSY 0x10000000
#define SMI_READ_VALID 0x08000000
#define SMI_OPCODE_READ 0x04000000
#define SMI_OPCODE_WRITE 0x00000000
#define ERR_INT_CAUSE 0x0080
#define ERR_INT_SMI_DONE 0x00000010
#define ERR_INT_MASK 0x0084
#define WINDOW_BASE(w) (0x0200 + ((w) << 3)) #define WINDOW_BASE(w) (0x0200 + ((w) << 3))
#define WINDOW_SIZE(w) (0x0204 + ((w) << 3)) #define WINDOW_SIZE(w) (0x0204 + ((w) << 3))
#define WINDOW_REMAP_HIGH(w) (0x0280 + ((w) << 2)) #define WINDOW_REMAP_HIGH(w) (0x0280 + ((w) << 2))
...@@ -104,16 +102,12 @@ static char mv643xx_eth_driver_version[] = "1.3"; ...@@ -104,16 +102,12 @@ static char mv643xx_eth_driver_version[] = "1.3";
#define TX_BW_MTU(p) (0x0458 + ((p) << 10)) #define TX_BW_MTU(p) (0x0458 + ((p) << 10))
#define TX_BW_BURST(p) (0x045c + ((p) << 10)) #define TX_BW_BURST(p) (0x045c + ((p) << 10))
#define INT_CAUSE(p) (0x0460 + ((p) << 10)) #define INT_CAUSE(p) (0x0460 + ((p) << 10))
#define INT_TX_END_0 0x00080000
#define INT_TX_END 0x07f80000 #define INT_TX_END 0x07f80000
#define INT_RX 0x0007fbfc #define INT_RX 0x000003fc
#define INT_EXT 0x00000002 #define INT_EXT 0x00000002
#define INT_CAUSE_EXT(p) (0x0464 + ((p) << 10)) #define INT_CAUSE_EXT(p) (0x0464 + ((p) << 10))
#define INT_EXT_LINK 0x00100000 #define INT_EXT_LINK_PHY 0x00110000
#define INT_EXT_PHY 0x00010000 #define INT_EXT_TX 0x000000ff
#define INT_EXT_TX_ERROR_0 0x00000100
#define INT_EXT_TX_0 0x00000001
#define INT_EXT_TX 0x0000ffff
#define INT_MASK(p) (0x0468 + ((p) << 10)) #define INT_MASK(p) (0x0468 + ((p) << 10))
#define INT_MASK_EXT(p) (0x046c + ((p) << 10)) #define INT_MASK_EXT(p) (0x046c + ((p) << 10))
#define TX_FIFO_URGENT_THRESHOLD(p) (0x0474 + ((p) << 10)) #define TX_FIFO_URGENT_THRESHOLD(p) (0x0474 + ((p) << 10))
...@@ -171,8 +165,8 @@ static char mv643xx_eth_driver_version[] = "1.3"; ...@@ -171,8 +165,8 @@ static char mv643xx_eth_driver_version[] = "1.3";
#define FORCE_LINK_PASS (1 << 1) #define FORCE_LINK_PASS (1 << 1)
#define SERIAL_PORT_ENABLE (1 << 0) #define SERIAL_PORT_ENABLE (1 << 0)
#define DEFAULT_RX_QUEUE_SIZE 400 #define DEFAULT_RX_QUEUE_SIZE 128
#define DEFAULT_TX_QUEUE_SIZE 800 #define DEFAULT_TX_QUEUE_SIZE 256
/* /*
...@@ -249,9 +243,23 @@ struct mv643xx_eth_shared_private { ...@@ -249,9 +243,23 @@ struct mv643xx_eth_shared_private {
void __iomem *base; void __iomem *base;
/* /*
* Protects access to SMI_REG, which is shared between ports. * Points at the right SMI instance to use.
*/
struct mv643xx_eth_shared_private *smi;
/*
* Provides access to local SMI interface.
*/
struct mii_bus smi_bus;
/*
* If we have access to the error interrupt pin (which is
* somewhat misnamed as it not only reflects internal errors
* but also reflects SMI completion), use that to wait for
* SMI access completion instead of polling the SMI busy bit.
*/ */
spinlock_t phy_lock; int err_interrupt;
wait_queue_head_t smi_busy_wait;
/* /*
* Per-port MBUS window access register value. * Per-port MBUS window access register value.
...@@ -263,9 +271,13 @@ struct mv643xx_eth_shared_private { ...@@ -263,9 +271,13 @@ struct mv643xx_eth_shared_private {
*/ */
unsigned int t_clk; unsigned int t_clk;
int extended_rx_coal_limit; int extended_rx_coal_limit;
int tx_bw_control_moved; int tx_bw_control;
}; };
#define TX_BW_CONTROL_ABSENT 0
#define TX_BW_CONTROL_OLD_LAYOUT 1
#define TX_BW_CONTROL_NEW_LAYOUT 2
/* per-port *****************************************************************/ /* per-port *****************************************************************/
struct mib_counters { struct mib_counters {
...@@ -314,8 +326,6 @@ struct rx_queue { ...@@ -314,8 +326,6 @@ struct rx_queue {
dma_addr_t rx_desc_dma; dma_addr_t rx_desc_dma;
int rx_desc_area_size; int rx_desc_area_size;
struct sk_buff **rx_skb; struct sk_buff **rx_skb;
struct timer_list rx_oom;
}; };
struct tx_queue { struct tx_queue {
...@@ -330,7 +340,12 @@ struct tx_queue { ...@@ -330,7 +340,12 @@ struct tx_queue {
struct tx_desc *tx_desc_area; struct tx_desc *tx_desc_area;
dma_addr_t tx_desc_dma; dma_addr_t tx_desc_dma;
int tx_desc_area_size; int tx_desc_area_size;
struct sk_buff **tx_skb;
struct sk_buff_head tx_skb;
unsigned long tx_packets;
unsigned long tx_bytes;
unsigned long tx_dropped;
}; };
struct mv643xx_eth_private { struct mv643xx_eth_private {
...@@ -339,14 +354,21 @@ struct mv643xx_eth_private { ...@@ -339,14 +354,21 @@ struct mv643xx_eth_private {
struct net_device *dev; struct net_device *dev;
struct mv643xx_eth_shared_private *shared_smi; struct phy_device *phy;
int phy_addr;
spinlock_t lock;
struct timer_list mib_counters_timer;
spinlock_t mib_counters_lock;
struct mib_counters mib_counters; struct mib_counters mib_counters;
struct work_struct tx_timeout_task; struct work_struct tx_timeout_task;
struct mii_if_info mii;
struct napi_struct napi;
u8 work_link;
u8 work_tx;
u8 work_tx_end;
u8 work_rx;
u8 work_rx_refill;
u8 work_rx_oom;
/* /*
* RX state. * RX state.
...@@ -354,9 +376,8 @@ struct mv643xx_eth_private { ...@@ -354,9 +376,8 @@ struct mv643xx_eth_private {
int default_rx_ring_size; int default_rx_ring_size;
unsigned long rx_desc_sram_addr; unsigned long rx_desc_sram_addr;
int rx_desc_sram_size; int rx_desc_sram_size;
u8 rxq_mask; int rxq_count;
int rxq_primary; struct timer_list rx_oom;
struct napi_struct napi;
struct rx_queue rxq[8]; struct rx_queue rxq[8];
/* /*
...@@ -365,12 +386,8 @@ struct mv643xx_eth_private { ...@@ -365,12 +386,8 @@ struct mv643xx_eth_private {
int default_tx_ring_size; int default_tx_ring_size;
unsigned long tx_desc_sram_addr; unsigned long tx_desc_sram_addr;
int tx_desc_sram_size; int tx_desc_sram_size;
u8 txq_mask; int txq_count;
int txq_primary;
struct tx_queue txq[8]; struct tx_queue txq[8];
#ifdef MV643XX_ETH_TX_FAST_REFILL
int tx_clean_threshold;
#endif
}; };
...@@ -440,94 +457,21 @@ static void txq_disable(struct tx_queue *txq) ...@@ -440,94 +457,21 @@ static void txq_disable(struct tx_queue *txq)
udelay(10); udelay(10);
} }
static void __txq_maybe_wake(struct tx_queue *txq) static void txq_maybe_wake(struct tx_queue *txq)
{ {
struct mv643xx_eth_private *mp = txq_to_mp(txq); struct mv643xx_eth_private *mp = txq_to_mp(txq);
struct netdev_queue *nq = netdev_get_tx_queue(mp->dev, txq->index);
/* if (netif_tx_queue_stopped(nq)) {
* netif_{stop,wake}_queue() flow control only applies to __netif_tx_lock(nq, smp_processor_id());
* the primary queue. if (txq->tx_ring_size - txq->tx_desc_count >= MAX_SKB_FRAGS + 1)
*/ netif_tx_wake_queue(nq);
BUG_ON(txq->index != mp->txq_primary); __netif_tx_unlock(nq);
if (txq->tx_ring_size - txq->tx_desc_count >= MAX_DESCS_PER_SKB)
netif_wake_queue(mp->dev);
}
/* rx ***********************************************************************/
static void txq_reclaim(struct tx_queue *txq, int force);
static void rxq_refill(struct rx_queue *rxq)
{
struct mv643xx_eth_private *mp = rxq_to_mp(rxq);
unsigned long flags;
spin_lock_irqsave(&mp->lock, flags);
while (rxq->rx_desc_count < rxq->rx_ring_size) {
int skb_size;
struct sk_buff *skb;
int unaligned;
int rx;
/*
* Reserve 2+14 bytes for an ethernet header (the
* hardware automatically prepends 2 bytes of dummy
* data to each received packet), 16 bytes for up to
* four VLAN tags, and 4 bytes for the trailing FCS
* -- 36 bytes total.
*/
skb_size = mp->dev->mtu + 36;
/*
* Make sure that the skb size is a multiple of 8
* bytes, as the lower three bits of the receive
* descriptor's buffer size field are ignored by
* the hardware.
*/
skb_size = (skb_size + 7) & ~7;
skb = dev_alloc_skb(skb_size + dma_get_cache_alignment() - 1);
if (skb == NULL)
break;
unaligned = (u32)skb->data & (dma_get_cache_alignment() - 1);
if (unaligned)
skb_reserve(skb, dma_get_cache_alignment() - unaligned);
rxq->rx_desc_count++;
rx = rxq->rx_used_desc;
rxq->rx_used_desc = (rx + 1) % rxq->rx_ring_size;
rxq->rx_desc_area[rx].buf_ptr = dma_map_single(NULL, skb->data,
skb_size, DMA_FROM_DEVICE);
rxq->rx_desc_area[rx].buf_size = skb_size;
rxq->rx_skb[rx] = skb;
wmb();
rxq->rx_desc_area[rx].cmd_sts = BUFFER_OWNED_BY_DMA |
RX_ENABLE_INTERRUPT;
wmb();
/*
* The hardware automatically prepends 2 bytes of
* dummy data to each received packet, so that the
* IP header ends up 16-byte aligned.
*/
skb_reserve(skb, 2);
} }
if (rxq->rx_desc_count != rxq->rx_ring_size)
mod_timer(&rxq->rx_oom, jiffies + (HZ / 10));
spin_unlock_irqrestore(&mp->lock, flags);
} }
static inline void rxq_refill_timer_wrapper(unsigned long data)
{
rxq_refill((struct rx_queue *)data);
}
/* rx napi ******************************************************************/
static int rxq_process(struct rx_queue *rxq, int budget) static int rxq_process(struct rx_queue *rxq, int budget)
{ {
struct mv643xx_eth_private *mp = rxq_to_mp(rxq); struct mv643xx_eth_private *mp = rxq_to_mp(rxq);
...@@ -539,31 +483,31 @@ static int rxq_process(struct rx_queue *rxq, int budget) ...@@ -539,31 +483,31 @@ static int rxq_process(struct rx_queue *rxq, int budget)
struct rx_desc *rx_desc; struct rx_desc *rx_desc;
unsigned int cmd_sts; unsigned int cmd_sts;
struct sk_buff *skb; struct sk_buff *skb;
unsigned long flags; u16 byte_cnt;
spin_lock_irqsave(&mp->lock, flags);
rx_desc = &rxq->rx_desc_area[rxq->rx_curr_desc]; rx_desc = &rxq->rx_desc_area[rxq->rx_curr_desc];
cmd_sts = rx_desc->cmd_sts; cmd_sts = rx_desc->cmd_sts;
if (cmd_sts & BUFFER_OWNED_BY_DMA) { if (cmd_sts & BUFFER_OWNED_BY_DMA)
spin_unlock_irqrestore(&mp->lock, flags);
break; break;
}
rmb(); rmb();
skb = rxq->rx_skb[rxq->rx_curr_desc]; skb = rxq->rx_skb[rxq->rx_curr_desc];
rxq->rx_skb[rxq->rx_curr_desc] = NULL; rxq->rx_skb[rxq->rx_curr_desc] = NULL;
rxq->rx_curr_desc = (rxq->rx_curr_desc + 1) % rxq->rx_ring_size; rxq->rx_curr_desc++;
if (rxq->rx_curr_desc == rxq->rx_ring_size)
spin_unlock_irqrestore(&mp->lock, flags); rxq->rx_curr_desc = 0;
dma_unmap_single(NULL, rx_desc->buf_ptr + 2, dma_unmap_single(NULL, rx_desc->buf_ptr,
rx_desc->buf_size, DMA_FROM_DEVICE); rx_desc->buf_size, DMA_FROM_DEVICE);
rxq->rx_desc_count--; rxq->rx_desc_count--;
rx++; rx++;
mp->work_rx_refill |= 1 << rxq->index;
byte_cnt = rx_desc->byte_cnt;
/* /*
* Update statistics. * Update statistics.
* *
...@@ -573,7 +517,7 @@ static int rxq_process(struct rx_queue *rxq, int budget) ...@@ -573,7 +517,7 @@ static int rxq_process(struct rx_queue *rxq, int budget)
* byte CRC at the end of the packet (which we do count). * byte CRC at the end of the packet (which we do count).
*/ */
stats->rx_packets++; stats->rx_packets++;
stats->rx_bytes += rx_desc->byte_cnt - 2; stats->rx_bytes += byte_cnt - 2;
/* /*
* In case we received a packet without first / last bits * In case we received a packet without first / last bits
...@@ -596,72 +540,96 @@ static int rxq_process(struct rx_queue *rxq, int budget) ...@@ -596,72 +540,96 @@ static int rxq_process(struct rx_queue *rxq, int budget)
if (cmd_sts & ERROR_SUMMARY) if (cmd_sts & ERROR_SUMMARY)
stats->rx_errors++; stats->rx_errors++;
dev_kfree_skb_irq(skb); dev_kfree_skb(skb);
} else { } else {
/* /*
* The -4 is for the CRC in the trailer of the * The -4 is for the CRC in the trailer of the
* received packet * received packet
*/ */
skb_put(skb, rx_desc->byte_cnt - 2 - 4); skb_put(skb, byte_cnt - 2 - 4);
if (cmd_sts & LAYER_4_CHECKSUM_OK) { if (cmd_sts & LAYER_4_CHECKSUM_OK)
skb->ip_summed = CHECKSUM_UNNECESSARY; skb->ip_summed = CHECKSUM_UNNECESSARY;
skb->csum = htons(
(cmd_sts & 0x0007fff8) >> 3);
}
skb->protocol = eth_type_trans(skb, mp->dev); skb->protocol = eth_type_trans(skb, mp->dev);
#ifdef MV643XX_ETH_NAPI
netif_receive_skb(skb); netif_receive_skb(skb);
#else
netif_rx(skb);
#endif
} }
mp->dev->last_rx = jiffies; mp->dev->last_rx = jiffies;
} }
rxq_refill(rxq); if (rx < budget)
mp->work_rx &= ~(1 << rxq->index);
return rx; return rx;
} }
#ifdef MV643XX_ETH_NAPI static int rxq_refill(struct rx_queue *rxq, int budget)
static int mv643xx_eth_poll(struct napi_struct *napi, int budget)
{ {
struct mv643xx_eth_private *mp; struct mv643xx_eth_private *mp = rxq_to_mp(rxq);
int rx; int skb_size;
int i; int refilled;
mp = container_of(napi, struct mv643xx_eth_private, napi); /*
* Reserve 2+14 bytes for an ethernet header (the hardware
* automatically prepends 2 bytes of dummy data to each
* received packet), 16 bytes for up to four VLAN tags, and
* 4 bytes for the trailing FCS -- 36 bytes total.
*/
skb_size = rxq_to_mp(rxq)->dev->mtu + 36;
#ifdef MV643XX_ETH_TX_FAST_REFILL /*
if (++mp->tx_clean_threshold > 5) { * Make sure that the skb size is a multiple of 8 bytes, as
mp->tx_clean_threshold = 0; * the lower three bits of the receive descriptor's buffer
for (i = 0; i < 8; i++) * size field are ignored by the hardware.
if (mp->txq_mask & (1 << i)) */
txq_reclaim(mp->txq + i, 0); skb_size = (skb_size + 7) & ~7;
if (netif_carrier_ok(mp->dev)) { refilled = 0;
spin_lock_irq(&mp->lock); while (refilled < budget && rxq->rx_desc_count < rxq->rx_ring_size) {
__txq_maybe_wake(mp->txq + mp->txq_primary); struct sk_buff *skb;
spin_unlock_irq(&mp->lock); int unaligned;
} int rx;
skb = dev_alloc_skb(skb_size + dma_get_cache_alignment() - 1);
if (skb == NULL) {
mp->work_rx_oom |= 1 << rxq->index;
goto oom;
} }
#endif
rx = 0; unaligned = (u32)skb->data & (dma_get_cache_alignment() - 1);
for (i = 7; rx < budget && i >= 0; i--) if (unaligned)
if (mp->rxq_mask & (1 << i)) skb_reserve(skb, dma_get_cache_alignment() - unaligned);
rx += rxq_process(mp->rxq + i, budget - rx);
if (rx < budget) { refilled++;
netif_rx_complete(mp->dev, napi); rxq->rx_desc_count++;
wrl(mp, INT_MASK(mp->port_num), INT_TX_END | INT_RX | INT_EXT);
rx = rxq->rx_used_desc++;
if (rxq->rx_used_desc == rxq->rx_ring_size)
rxq->rx_used_desc = 0;
rxq->rx_desc_area[rx].buf_ptr = dma_map_single(NULL, skb->data,
skb_size, DMA_FROM_DEVICE);
rxq->rx_desc_area[rx].buf_size = skb_size;
rxq->rx_skb[rx] = skb;
wmb();
rxq->rx_desc_area[rx].cmd_sts = BUFFER_OWNED_BY_DMA |
RX_ENABLE_INTERRUPT;
wmb();
/*
* The hardware automatically prepends 2 bytes of
* dummy data to each received packet, so that the
* IP header ends up 16-byte aligned.
*/
skb_reserve(skb, 2);
} }
return rx; if (refilled < budget)
mp->work_rx_refill &= ~(1 << rxq->index);
oom:
return refilled;
} }
#endif
/* tx ***********************************************************************/ /* tx ***********************************************************************/
...@@ -684,8 +652,9 @@ static int txq_alloc_desc_index(struct tx_queue *txq) ...@@ -684,8 +652,9 @@ static int txq_alloc_desc_index(struct tx_queue *txq)
BUG_ON(txq->tx_desc_count >= txq->tx_ring_size); BUG_ON(txq->tx_desc_count >= txq->tx_ring_size);
tx_desc_curr = txq->tx_curr_desc; tx_desc_curr = txq->tx_curr_desc++;
txq->tx_curr_desc = (tx_desc_curr + 1) % txq->tx_ring_size; if (txq->tx_curr_desc == txq->tx_ring_size)
txq->tx_curr_desc = 0;
BUG_ON(txq->tx_curr_desc == txq->tx_used_desc); BUG_ON(txq->tx_curr_desc == txq->tx_used_desc);
...@@ -714,10 +683,8 @@ static void txq_submit_frag_skb(struct tx_queue *txq, struct sk_buff *skb) ...@@ -714,10 +683,8 @@ static void txq_submit_frag_skb(struct tx_queue *txq, struct sk_buff *skb)
desc->cmd_sts = BUFFER_OWNED_BY_DMA | desc->cmd_sts = BUFFER_OWNED_BY_DMA |
ZERO_PADDING | TX_LAST_DESC | ZERO_PADDING | TX_LAST_DESC |
TX_ENABLE_INTERRUPT; TX_ENABLE_INTERRUPT;
txq->tx_skb[tx_index] = skb;
} else { } else {
desc->cmd_sts = BUFFER_OWNED_BY_DMA; desc->cmd_sts = BUFFER_OWNED_BY_DMA;
txq->tx_skb[tx_index] = NULL;
} }
desc->l4i_chk = 0; desc->l4i_chk = 0;
...@@ -734,144 +701,222 @@ static inline __be16 sum16_as_be(__sum16 sum) ...@@ -734,144 +701,222 @@ static inline __be16 sum16_as_be(__sum16 sum)
return (__force __be16)sum; return (__force __be16)sum;
} }
static void txq_submit_skb(struct tx_queue *txq, struct sk_buff *skb) static int txq_submit_skb(struct tx_queue *txq, struct sk_buff *skb)
{ {
struct mv643xx_eth_private *mp = txq_to_mp(txq); struct mv643xx_eth_private *mp = txq_to_mp(txq);
int nr_frags = skb_shinfo(skb)->nr_frags; int nr_frags = skb_shinfo(skb)->nr_frags;
int tx_index; int tx_index;
struct tx_desc *desc; struct tx_desc *desc;
u32 cmd_sts; u32 cmd_sts;
u16 l4i_chk;
int length; int length;
cmd_sts = TX_FIRST_DESC | GEN_CRC | BUFFER_OWNED_BY_DMA; cmd_sts = TX_FIRST_DESC | GEN_CRC | BUFFER_OWNED_BY_DMA;
l4i_chk = 0;
tx_index = txq_alloc_desc_index(txq);
desc = &txq->tx_desc_area[tx_index];
if (nr_frags) {
txq_submit_frag_skb(txq, skb);
length = skb_headlen(skb);
txq->tx_skb[tx_index] = NULL;
} else {
cmd_sts |= ZERO_PADDING | TX_LAST_DESC | TX_ENABLE_INTERRUPT;
length = skb->len;
txq->tx_skb[tx_index] = skb;
}
desc->byte_cnt = length;
desc->buf_ptr = dma_map_single(NULL, skb->data, length, DMA_TO_DEVICE);
if (skb->ip_summed == CHECKSUM_PARTIAL) { if (skb->ip_summed == CHECKSUM_PARTIAL) {
int mac_hdr_len; int tag_bytes;
BUG_ON(skb->protocol != htons(ETH_P_IP) && BUG_ON(skb->protocol != htons(ETH_P_IP) &&
skb->protocol != htons(ETH_P_8021Q)); skb->protocol != htons(ETH_P_8021Q));
cmd_sts |= GEN_TCP_UDP_CHECKSUM | tag_bytes = (void *)ip_hdr(skb) - (void *)skb->data - ETH_HLEN;
GEN_IP_V4_CHECKSUM | if (unlikely(tag_bytes & ~12)) {
ip_hdr(skb)->ihl << TX_IHL_SHIFT; if (skb_checksum_help(skb) == 0)
goto no_csum;
kfree_skb(skb);
return 1;
}
mac_hdr_len = (void *)ip_hdr(skb) - (void *)skb->data; if (tag_bytes & 4)
switch (mac_hdr_len - ETH_HLEN) {
case 0:
break;
case 4:
cmd_sts |= MAC_HDR_EXTRA_4_BYTES;
break;
case 8:
cmd_sts |= MAC_HDR_EXTRA_8_BYTES;
break;
case 12:
cmd_sts |= MAC_HDR_EXTRA_4_BYTES; cmd_sts |= MAC_HDR_EXTRA_4_BYTES;
if (tag_bytes & 8)
cmd_sts |= MAC_HDR_EXTRA_8_BYTES; cmd_sts |= MAC_HDR_EXTRA_8_BYTES;
break;
default: cmd_sts |= GEN_TCP_UDP_CHECKSUM |
if (net_ratelimit()) GEN_IP_V4_CHECKSUM |
dev_printk(KERN_ERR, &txq_to_mp(txq)->dev->dev, ip_hdr(skb)->ihl << TX_IHL_SHIFT;
"mac header length is %d?!\n", mac_hdr_len);
break;
}
switch (ip_hdr(skb)->protocol) { switch (ip_hdr(skb)->protocol) {
case IPPROTO_UDP: case IPPROTO_UDP:
cmd_sts |= UDP_FRAME; cmd_sts |= UDP_FRAME;
desc->l4i_chk = ntohs(sum16_as_be(udp_hdr(skb)->check)); l4i_chk = ntohs(sum16_as_be(udp_hdr(skb)->check));
break; break;
case IPPROTO_TCP: case IPPROTO_TCP:
desc->l4i_chk = ntohs(sum16_as_be(tcp_hdr(skb)->check)); l4i_chk = ntohs(sum16_as_be(tcp_hdr(skb)->check));
break; break;
default: default:
BUG(); BUG();
} }
} else { } else {
no_csum:
/* Errata BTS #50, IHL must be 5 if no HW checksum */ /* Errata BTS #50, IHL must be 5 if no HW checksum */
cmd_sts |= 5 << TX_IHL_SHIFT; cmd_sts |= 5 << TX_IHL_SHIFT;
desc->l4i_chk = 0;
} }
tx_index = txq_alloc_desc_index(txq);
desc = &txq->tx_desc_area[tx_index];
if (nr_frags) {
txq_submit_frag_skb(txq, skb);
length = skb_headlen(skb);
} else {
cmd_sts |= ZERO_PADDING | TX_LAST_DESC | TX_ENABLE_INTERRUPT;
length = skb->len;
}
desc->l4i_chk = l4i_chk;
desc->byte_cnt = length;
desc->buf_ptr = dma_map_single(NULL, skb->data, length, DMA_TO_DEVICE);
__skb_queue_tail(&txq->tx_skb, skb);
/* ensure all other descriptors are written before first cmd_sts */ /* ensure all other descriptors are written before first cmd_sts */
wmb(); wmb();
desc->cmd_sts = cmd_sts; desc->cmd_sts = cmd_sts;
/* clear TX_END interrupt status */ /* clear TX_END status */
wrl(mp, INT_CAUSE(mp->port_num), ~(INT_TX_END_0 << txq->index)); mp->work_tx_end &= ~(1 << txq->index);
rdl(mp, INT_CAUSE(mp->port_num));
/* ensure all descriptors are written before poking hardware */ /* ensure all descriptors are written before poking hardware */
wmb(); wmb();
txq_enable(txq); txq_enable(txq);
txq->tx_desc_count += nr_frags + 1; txq->tx_desc_count += nr_frags + 1;
return 0;
} }
static int mv643xx_eth_xmit(struct sk_buff *skb, struct net_device *dev) static int mv643xx_eth_xmit(struct sk_buff *skb, struct net_device *dev)
{ {
struct mv643xx_eth_private *mp = netdev_priv(dev); struct mv643xx_eth_private *mp = netdev_priv(dev);
struct net_device_stats *stats = &dev->stats; int queue;
struct tx_queue *txq; struct tx_queue *txq;
unsigned long flags; struct netdev_queue *nq;
queue = skb_get_queue_mapping(skb);
txq = mp->txq + queue;
nq = netdev_get_tx_queue(dev, queue);
if (has_tiny_unaligned_frags(skb) && __skb_linearize(skb)) { if (has_tiny_unaligned_frags(skb) && __skb_linearize(skb)) {
stats->tx_dropped++; txq->tx_dropped++;
dev_printk(KERN_DEBUG, &dev->dev, dev_printk(KERN_DEBUG, &dev->dev,
"failed to linearize skb with tiny " "failed to linearize skb with tiny "
"unaligned fragment\n"); "unaligned fragment\n");
return NETDEV_TX_BUSY; return NETDEV_TX_BUSY;
} }
spin_lock_irqsave(&mp->lock, flags); if (txq->tx_ring_size - txq->tx_desc_count < MAX_SKB_FRAGS + 1) {
if (net_ratelimit())
txq = mp->txq + mp->txq_primary; dev_printk(KERN_ERR, &dev->dev, "tx queue full?!\n");
if (txq->tx_ring_size - txq->tx_desc_count < MAX_DESCS_PER_SKB) {
spin_unlock_irqrestore(&mp->lock, flags);
if (txq->index == mp->txq_primary && net_ratelimit())
dev_printk(KERN_ERR, &dev->dev,
"primary tx queue full?!\n");
kfree_skb(skb); kfree_skb(skb);
return NETDEV_TX_OK; return NETDEV_TX_OK;
} }
txq_submit_skb(txq, skb); if (!txq_submit_skb(txq, skb)) {
stats->tx_bytes += skb->len;
stats->tx_packets++;
dev->trans_start = jiffies;
if (txq->index == mp->txq_primary) {
int entries_left; int entries_left;
txq->tx_bytes += skb->len;
txq->tx_packets++;
dev->trans_start = jiffies;
entries_left = txq->tx_ring_size - txq->tx_desc_count; entries_left = txq->tx_ring_size - txq->tx_desc_count;
if (entries_left < MAX_DESCS_PER_SKB) if (entries_left < MAX_SKB_FRAGS + 1)
netif_stop_queue(dev); netif_tx_stop_queue(nq);
} }
spin_unlock_irqrestore(&mp->lock, flags);
return NETDEV_TX_OK; return NETDEV_TX_OK;
} }
/* tx napi ******************************************************************/
static void txq_kick(struct tx_queue *txq)
{
struct mv643xx_eth_private *mp = txq_to_mp(txq);
struct netdev_queue *nq = netdev_get_tx_queue(mp->dev, txq->index);
u32 hw_desc_ptr;
u32 expected_ptr;
__netif_tx_lock(nq, smp_processor_id());
if (rdl(mp, TXQ_COMMAND(mp->port_num)) & (1 << txq->index))
goto out;
hw_desc_ptr = rdl(mp, TXQ_CURRENT_DESC_PTR(mp->port_num, txq->index));
expected_ptr = (u32)txq->tx_desc_dma +
txq->tx_curr_desc * sizeof(struct tx_desc);
if (hw_desc_ptr != expected_ptr)
txq_enable(txq);
out:
__netif_tx_unlock(nq);
mp->work_tx_end &= ~(1 << txq->index);
}
static int txq_reclaim(struct tx_queue *txq, int budget, int force)
{
struct mv643xx_eth_private *mp = txq_to_mp(txq);
struct netdev_queue *nq = netdev_get_tx_queue(mp->dev, txq->index);
int reclaimed;
__netif_tx_lock(nq, smp_processor_id());
reclaimed = 0;
while (reclaimed < budget && txq->tx_desc_count > 0) {
int tx_index;
struct tx_desc *desc;
u32 cmd_sts;
struct sk_buff *skb;
tx_index = txq->tx_used_desc;
desc = &txq->tx_desc_area[tx_index];
cmd_sts = desc->cmd_sts;
if (cmd_sts & BUFFER_OWNED_BY_DMA) {
if (!force)
break;
desc->cmd_sts = cmd_sts & ~BUFFER_OWNED_BY_DMA;
}
txq->tx_used_desc = tx_index + 1;
if (txq->tx_used_desc == txq->tx_ring_size)
txq->tx_used_desc = 0;
reclaimed++;
txq->tx_desc_count--;
skb = NULL;
if (cmd_sts & TX_LAST_DESC)
skb = __skb_dequeue(&txq->tx_skb);
if (cmd_sts & ERROR_SUMMARY) {
dev_printk(KERN_INFO, &mp->dev->dev, "tx error\n");
mp->dev->stats.tx_errors++;
}
if (cmd_sts & TX_FIRST_DESC) {
dma_unmap_single(NULL, desc->buf_ptr,
desc->byte_cnt, DMA_TO_DEVICE);
} else {
dma_unmap_page(NULL, desc->buf_ptr,
desc->byte_cnt, DMA_TO_DEVICE);
}
if (skb)
dev_kfree_skb(skb);
}
__netif_tx_unlock(nq);
if (reclaimed < budget)
mp->work_tx &= ~(1 << txq->index);
return reclaimed;
}
/* tx rate control **********************************************************/ /* tx rate control **********************************************************/
/* /*
* Set total maximum TX rate (shared by all TX queues for this port) * Set total maximum TX rate (shared by all TX queues for this port)
...@@ -895,14 +940,17 @@ static void tx_set_rate(struct mv643xx_eth_private *mp, int rate, int burst) ...@@ -895,14 +940,17 @@ static void tx_set_rate(struct mv643xx_eth_private *mp, int rate, int burst)
if (bucket_size > 65535) if (bucket_size > 65535)
bucket_size = 65535; bucket_size = 65535;
if (mp->shared->tx_bw_control_moved) { switch (mp->shared->tx_bw_control) {
wrl(mp, TX_BW_RATE_MOVED(mp->port_num), token_rate); case TX_BW_CONTROL_OLD_LAYOUT:
wrl(mp, TX_BW_MTU_MOVED(mp->port_num), mtu);
wrl(mp, TX_BW_BURST_MOVED(mp->port_num), bucket_size);
} else {
wrl(mp, TX_BW_RATE(mp->port_num), token_rate); wrl(mp, TX_BW_RATE(mp->port_num), token_rate);
wrl(mp, TX_BW_MTU(mp->port_num), mtu); wrl(mp, TX_BW_MTU(mp->port_num), mtu);
wrl(mp, TX_BW_BURST(mp->port_num), bucket_size); wrl(mp, TX_BW_BURST(mp->port_num), bucket_size);
break;
case TX_BW_CONTROL_NEW_LAYOUT:
wrl(mp, TX_BW_RATE_MOVED(mp->port_num), token_rate);
wrl(mp, TX_BW_MTU_MOVED(mp->port_num), mtu);
wrl(mp, TX_BW_BURST_MOVED(mp->port_num), bucket_size);
break;
} }
} }
...@@ -934,14 +982,21 @@ static void txq_set_fixed_prio_mode(struct tx_queue *txq) ...@@ -934,14 +982,21 @@ static void txq_set_fixed_prio_mode(struct tx_queue *txq)
/* /*
* Turn on fixed priority mode. * Turn on fixed priority mode.
*/ */
if (mp->shared->tx_bw_control_moved) off = 0;
off = TXQ_FIX_PRIO_CONF_MOVED(mp->port_num); switch (mp->shared->tx_bw_control) {
else case TX_BW_CONTROL_OLD_LAYOUT:
off = TXQ_FIX_PRIO_CONF(mp->port_num); off = TXQ_FIX_PRIO_CONF(mp->port_num);
break;
case TX_BW_CONTROL_NEW_LAYOUT:
off = TXQ_FIX_PRIO_CONF_MOVED(mp->port_num);
break;
}
if (off) {
val = rdl(mp, off); val = rdl(mp, off);
val |= 1 << txq->index; val |= 1 << txq->index;
wrl(mp, off, val); wrl(mp, off, val);
}
} }
static void txq_set_wrr(struct tx_queue *txq, int weight) static void txq_set_wrr(struct tx_queue *txq, int weight)
...@@ -953,11 +1008,17 @@ static void txq_set_wrr(struct tx_queue *txq, int weight) ...@@ -953,11 +1008,17 @@ static void txq_set_wrr(struct tx_queue *txq, int weight)
/* /*
* Turn off fixed priority mode. * Turn off fixed priority mode.
*/ */
if (mp->shared->tx_bw_control_moved) off = 0;
off = TXQ_FIX_PRIO_CONF_MOVED(mp->port_num); switch (mp->shared->tx_bw_control) {
else case TX_BW_CONTROL_OLD_LAYOUT:
off = TXQ_FIX_PRIO_CONF(mp->port_num); off = TXQ_FIX_PRIO_CONF(mp->port_num);
break;
case TX_BW_CONTROL_NEW_LAYOUT:
off = TXQ_FIX_PRIO_CONF_MOVED(mp->port_num);
break;
}
if (off) {
val = rdl(mp, off); val = rdl(mp, off);
val &= ~(1 << txq->index); val &= ~(1 << txq->index);
wrl(mp, off, val); wrl(mp, off, val);
...@@ -970,78 +1031,124 @@ static void txq_set_wrr(struct tx_queue *txq, int weight) ...@@ -970,78 +1031,124 @@ static void txq_set_wrr(struct tx_queue *txq, int weight)
val = rdl(mp, off); val = rdl(mp, off);
val = (val & ~0xff) | (weight & 0xff); val = (val & ~0xff) | (weight & 0xff);
wrl(mp, off, val); wrl(mp, off, val);
}
} }
/* mii management interface *************************************************/ /* mii management interface *************************************************/
#define SMI_BUSY 0x10000000 static irqreturn_t mv643xx_eth_err_irq(int irq, void *dev_id)
#define SMI_READ_VALID 0x08000000 {
#define SMI_OPCODE_READ 0x04000000 struct mv643xx_eth_shared_private *msp = dev_id;
#define SMI_OPCODE_WRITE 0x00000000
static void smi_reg_read(struct mv643xx_eth_private *mp, unsigned int addr, if (readl(msp->base + ERR_INT_CAUSE) & ERR_INT_SMI_DONE) {
unsigned int reg, unsigned int *value) writel(~ERR_INT_SMI_DONE, msp->base + ERR_INT_CAUSE);
wake_up(&msp->smi_busy_wait);
return IRQ_HANDLED;
}
return IRQ_NONE;
}
static int smi_is_done(struct mv643xx_eth_shared_private *msp)
{
return !(readl(msp->base + SMI_REG) & SMI_BUSY);
}
static int smi_wait_ready(struct mv643xx_eth_shared_private *msp)
{ {
void __iomem *smi_reg = mp->shared_smi->base + SMI_REG; if (msp->err_interrupt == NO_IRQ) {
unsigned long flags;
int i; int i;
/* the SMI register is a shared resource */ for (i = 0; !smi_is_done(msp); i++) {
spin_lock_irqsave(&mp->shared_smi->phy_lock, flags); if (i == 10)
return -ETIMEDOUT;
msleep(10);
}
/* wait for the SMI register to become available */ return 0;
for (i = 0; readl(smi_reg) & SMI_BUSY; i++) {
if (i == 1000) {
printk("%s: PHY busy timeout\n", mp->dev->name);
goto out;
} }
udelay(10);
if (!wait_event_timeout(msp->smi_busy_wait, smi_is_done(msp),
msecs_to_jiffies(100)))
return -ETIMEDOUT;
return 0;
}
static int smi_bus_read(struct mii_bus *bus, int addr, int reg)
{
struct mv643xx_eth_shared_private *msp = bus->priv;
void __iomem *smi_reg = msp->base + SMI_REG;
int ret;
if (smi_wait_ready(msp)) {
printk("mv643xx_eth: SMI bus busy timeout\n");
return -ETIMEDOUT;
} }
writel(SMI_OPCODE_READ | (reg << 21) | (addr << 16), smi_reg); writel(SMI_OPCODE_READ | (reg << 21) | (addr << 16), smi_reg);
/* now wait for the data to be valid */ if (smi_wait_ready(msp)) {
for (i = 0; !(readl(smi_reg) & SMI_READ_VALID); i++) { printk("mv643xx_eth: SMI bus busy timeout\n");
if (i == 1000) { return -ETIMEDOUT;
printk("%s: PHY read timeout\n", mp->dev->name);
goto out;
} }
udelay(10);
ret = readl(smi_reg);
if (!(ret & SMI_READ_VALID)) {
printk("mv643xx_eth: SMI bus read not valid\n");
return -ENODEV;
} }
*value = readl(smi_reg) & 0xffff; return ret & 0xffff;
out:
spin_unlock_irqrestore(&mp->shared_smi->phy_lock, flags);
} }
static void smi_reg_write(struct mv643xx_eth_private *mp, static int smi_bus_write(struct mii_bus *bus, int addr, int reg, u16 val)
unsigned int addr,
unsigned int reg, unsigned int value)
{ {
void __iomem *smi_reg = mp->shared_smi->base + SMI_REG; struct mv643xx_eth_shared_private *msp = bus->priv;
unsigned long flags; void __iomem *smi_reg = msp->base + SMI_REG;
int i;
/* the SMI register is a shared resource */
spin_lock_irqsave(&mp->shared_smi->phy_lock, flags);
/* wait for the SMI register to become available */ if (smi_wait_ready(msp)) {
for (i = 0; readl(smi_reg) & SMI_BUSY; i++) { printk("mv643xx_eth: SMI bus busy timeout\n");
if (i == 1000) { return -ETIMEDOUT;
printk("%s: PHY busy timeout\n", mp->dev->name);
goto out;
}
udelay(10);
} }
writel(SMI_OPCODE_WRITE | (reg << 21) | writel(SMI_OPCODE_WRITE | (reg << 21) |
(addr << 16) | (value & 0xffff), smi_reg); (addr << 16) | (val & 0xffff), smi_reg);
out:
spin_unlock_irqrestore(&mp->shared_smi->phy_lock, flags); if (smi_wait_ready(msp)) {
printk("mv643xx_eth: SMI bus busy timeout\n");
return -ETIMEDOUT;
}
return 0;
} }
/* mib counters *************************************************************/ /* statistics ***************************************************************/
static struct net_device_stats *mv643xx_eth_get_stats(struct net_device *dev)
{
struct mv643xx_eth_private *mp = netdev_priv(dev);
struct net_device_stats *stats = &dev->stats;
unsigned long tx_packets = 0;
unsigned long tx_bytes = 0;
unsigned long tx_dropped = 0;
int i;
for (i = 0; i < mp->txq_count; i++) {
struct tx_queue *txq = mp->txq + i;
tx_packets += txq->tx_packets;
tx_bytes += txq->tx_bytes;
tx_dropped += txq->tx_dropped;
}
stats->tx_packets = tx_packets;
stats->tx_bytes = tx_bytes;
stats->tx_dropped = tx_dropped;
return stats;
}
static inline u32 mib_read(struct mv643xx_eth_private *mp, int offset) static inline u32 mib_read(struct mv643xx_eth_private *mp, int offset)
{ {
return rdl(mp, MIB_COUNTERS(mp->port_num) + offset); return rdl(mp, MIB_COUNTERS(mp->port_num) + offset);
...@@ -1059,6 +1166,7 @@ static void mib_counters_update(struct mv643xx_eth_private *mp) ...@@ -1059,6 +1166,7 @@ static void mib_counters_update(struct mv643xx_eth_private *mp)
{ {
struct mib_counters *p = &mp->mib_counters; struct mib_counters *p = &mp->mib_counters;
spin_lock(&mp->mib_counters_lock);
p->good_octets_received += mib_read(mp, 0x00); p->good_octets_received += mib_read(mp, 0x00);
p->good_octets_received += (u64)mib_read(mp, 0x04) << 32; p->good_octets_received += (u64)mib_read(mp, 0x04) << 32;
p->bad_octets_received += mib_read(mp, 0x08); p->bad_octets_received += mib_read(mp, 0x08);
...@@ -1091,6 +1199,16 @@ static void mib_counters_update(struct mv643xx_eth_private *mp) ...@@ -1091,6 +1199,16 @@ static void mib_counters_update(struct mv643xx_eth_private *mp)
p->bad_crc_event += mib_read(mp, 0x74); p->bad_crc_event += mib_read(mp, 0x74);
p->collision += mib_read(mp, 0x78); p->collision += mib_read(mp, 0x78);
p->late_collision += mib_read(mp, 0x7c); p->late_collision += mib_read(mp, 0x7c);
spin_unlock(&mp->mib_counters_lock);
mod_timer(&mp->mib_counters_timer, jiffies + 30 * HZ);
}
static void mib_counters_timer_wrapper(unsigned long _mp)
{
struct mv643xx_eth_private *mp = (void *)_mp;
mib_counters_update(mp);
} }
...@@ -1156,9 +1274,9 @@ static int mv643xx_eth_get_settings(struct net_device *dev, struct ethtool_cmd * ...@@ -1156,9 +1274,9 @@ static int mv643xx_eth_get_settings(struct net_device *dev, struct ethtool_cmd *
struct mv643xx_eth_private *mp = netdev_priv(dev); struct mv643xx_eth_private *mp = netdev_priv(dev);
int err; int err;
spin_lock_irq(&mp->lock); err = phy_read_status(mp->phy);
err = mii_ethtool_gset(&mp->mii, cmd); if (err == 0)
spin_unlock_irq(&mp->lock); err = phy_ethtool_gset(mp->phy, cmd);
/* /*
* The MAC does not support 1000baseT_Half. * The MAC does not support 1000baseT_Half.
...@@ -1206,18 +1324,13 @@ static int mv643xx_eth_get_settings_phyless(struct net_device *dev, struct ethto ...@@ -1206,18 +1324,13 @@ static int mv643xx_eth_get_settings_phyless(struct net_device *dev, struct ethto
static int mv643xx_eth_set_settings(struct net_device *dev, struct ethtool_cmd *cmd) static int mv643xx_eth_set_settings(struct net_device *dev, struct ethtool_cmd *cmd)
{ {
struct mv643xx_eth_private *mp = netdev_priv(dev); struct mv643xx_eth_private *mp = netdev_priv(dev);
int err;
/* /*
* The MAC does not support 1000baseT_Half. * The MAC does not support 1000baseT_Half.
*/ */
cmd->advertising &= ~ADVERTISED_1000baseT_Half; cmd->advertising &= ~ADVERTISED_1000baseT_Half;
spin_lock_irq(&mp->lock); return phy_ethtool_sset(mp->phy, cmd);
err = mii_ethtool_sset(&mp->mii, cmd);
spin_unlock_irq(&mp->lock);
return err;
} }
static int mv643xx_eth_set_settings_phyless(struct net_device *dev, struct ethtool_cmd *cmd) static int mv643xx_eth_set_settings_phyless(struct net_device *dev, struct ethtool_cmd *cmd)
...@@ -1239,7 +1352,7 @@ static int mv643xx_eth_nway_reset(struct net_device *dev) ...@@ -1239,7 +1352,7 @@ static int mv643xx_eth_nway_reset(struct net_device *dev)
{ {
struct mv643xx_eth_private *mp = netdev_priv(dev); struct mv643xx_eth_private *mp = netdev_priv(dev);
return mii_nway_restart(&mp->mii); return genphy_restart_aneg(mp->phy);
} }
static int mv643xx_eth_nway_reset_phyless(struct net_device *dev) static int mv643xx_eth_nway_reset_phyless(struct net_device *dev)
...@@ -1249,14 +1362,7 @@ static int mv643xx_eth_nway_reset_phyless(struct net_device *dev) ...@@ -1249,14 +1362,7 @@ static int mv643xx_eth_nway_reset_phyless(struct net_device *dev)
static u32 mv643xx_eth_get_link(struct net_device *dev) static u32 mv643xx_eth_get_link(struct net_device *dev)
{ {
struct mv643xx_eth_private *mp = netdev_priv(dev); return !!netif_carrier_ok(dev);
return mii_link_ok(&mp->mii);
}
static u32 mv643xx_eth_get_link_phyless(struct net_device *dev)
{
return 1;
} }
static void mv643xx_eth_get_strings(struct net_device *dev, static void mv643xx_eth_get_strings(struct net_device *dev,
...@@ -1277,9 +1383,10 @@ static void mv643xx_eth_get_ethtool_stats(struct net_device *dev, ...@@ -1277,9 +1383,10 @@ static void mv643xx_eth_get_ethtool_stats(struct net_device *dev,
struct ethtool_stats *stats, struct ethtool_stats *stats,
uint64_t *data) uint64_t *data)
{ {
struct mv643xx_eth_private *mp = dev->priv; struct mv643xx_eth_private *mp = netdev_priv(dev);
int i; int i;
mv643xx_eth_get_stats(dev);
mib_counters_update(mp); mib_counters_update(mp);
for (i = 0; i < ARRAY_SIZE(mv643xx_eth_stats); i++) { for (i = 0; i < ARRAY_SIZE(mv643xx_eth_stats); i++) {
...@@ -1323,7 +1430,7 @@ static const struct ethtool_ops mv643xx_eth_ethtool_ops_phyless = { ...@@ -1323,7 +1430,7 @@ static const struct ethtool_ops mv643xx_eth_ethtool_ops_phyless = {
.set_settings = mv643xx_eth_set_settings_phyless, .set_settings = mv643xx_eth_set_settings_phyless,
.get_drvinfo = mv643xx_eth_get_drvinfo, .get_drvinfo = mv643xx_eth_get_drvinfo,
.nway_reset = mv643xx_eth_nway_reset_phyless, .nway_reset = mv643xx_eth_nway_reset_phyless,
.get_link = mv643xx_eth_get_link_phyless, .get_link = mv643xx_eth_get_link,
.set_sg = ethtool_op_set_sg, .set_sg = ethtool_op_set_sg,
.get_strings = mv643xx_eth_get_strings, .get_strings = mv643xx_eth_get_strings,
.get_ethtool_stats = mv643xx_eth_get_ethtool_stats, .get_ethtool_stats = mv643xx_eth_get_ethtool_stats,
...@@ -1487,7 +1594,7 @@ static int rxq_init(struct mv643xx_eth_private *mp, int index) ...@@ -1487,7 +1594,7 @@ static int rxq_init(struct mv643xx_eth_private *mp, int index)
size = rxq->rx_ring_size * sizeof(struct rx_desc); size = rxq->rx_ring_size * sizeof(struct rx_desc);
if (index == mp->rxq_primary && size <= mp->rx_desc_sram_size) { if (index == 0 && size <= mp->rx_desc_sram_size) {
rxq->rx_desc_area = ioremap(mp->rx_desc_sram_addr, rxq->rx_desc_area = ioremap(mp->rx_desc_sram_addr,
mp->rx_desc_sram_size); mp->rx_desc_sram_size);
rxq->rx_desc_dma = mp->rx_desc_sram_addr; rxq->rx_desc_dma = mp->rx_desc_sram_addr;
...@@ -1515,20 +1622,21 @@ static int rxq_init(struct mv643xx_eth_private *mp, int index) ...@@ -1515,20 +1622,21 @@ static int rxq_init(struct mv643xx_eth_private *mp, int index)
rx_desc = (struct rx_desc *)rxq->rx_desc_area; rx_desc = (struct rx_desc *)rxq->rx_desc_area;
for (i = 0; i < rxq->rx_ring_size; i++) { for (i = 0; i < rxq->rx_ring_size; i++) {
int nexti = (i + 1) % rxq->rx_ring_size; int nexti;
nexti = i + 1;
if (nexti == rxq->rx_ring_size)
nexti = 0;
rx_desc[i].next_desc_ptr = rxq->rx_desc_dma + rx_desc[i].next_desc_ptr = rxq->rx_desc_dma +
nexti * sizeof(struct rx_desc); nexti * sizeof(struct rx_desc);
} }
init_timer(&rxq->rx_oom);
rxq->rx_oom.data = (unsigned long)rxq;
rxq->rx_oom.function = rxq_refill_timer_wrapper;
return 0; return 0;
out_free: out_free:
if (index == mp->rxq_primary && size <= mp->rx_desc_sram_size) if (index == 0 && size <= mp->rx_desc_sram_size)
iounmap(rxq->rx_desc_area); iounmap(rxq->rx_desc_area);
else else
dma_free_coherent(NULL, size, dma_free_coherent(NULL, size,
...@@ -1546,8 +1654,6 @@ static void rxq_deinit(struct rx_queue *rxq) ...@@ -1546,8 +1654,6 @@ static void rxq_deinit(struct rx_queue *rxq)
rxq_disable(rxq); rxq_disable(rxq);
del_timer_sync(&rxq->rx_oom);
for (i = 0; i < rxq->rx_ring_size; i++) { for (i = 0; i < rxq->rx_ring_size; i++) {
if (rxq->rx_skb[i]) { if (rxq->rx_skb[i]) {
dev_kfree_skb(rxq->rx_skb[i]); dev_kfree_skb(rxq->rx_skb[i]);
...@@ -1561,7 +1667,7 @@ static void rxq_deinit(struct rx_queue *rxq) ...@@ -1561,7 +1667,7 @@ static void rxq_deinit(struct rx_queue *rxq)
rxq->rx_desc_count); rxq->rx_desc_count);
} }
if (rxq->index == mp->rxq_primary && if (rxq->index == 0 &&
rxq->rx_desc_area_size <= mp->rx_desc_sram_size) rxq->rx_desc_area_size <= mp->rx_desc_sram_size)
iounmap(rxq->rx_desc_area); iounmap(rxq->rx_desc_area);
else else
...@@ -1588,7 +1694,7 @@ static int txq_init(struct mv643xx_eth_private *mp, int index) ...@@ -1588,7 +1694,7 @@ static int txq_init(struct mv643xx_eth_private *mp, int index)
size = txq->tx_ring_size * sizeof(struct tx_desc); size = txq->tx_ring_size * sizeof(struct tx_desc);
if (index == mp->txq_primary && size <= mp->tx_desc_sram_size) { if (index == 0 && size <= mp->tx_desc_sram_size) {
txq->tx_desc_area = ioremap(mp->tx_desc_sram_addr, txq->tx_desc_area = ioremap(mp->tx_desc_sram_addr,
mp->tx_desc_sram_size); mp->tx_desc_sram_size);
txq->tx_desc_dma = mp->tx_desc_sram_addr; txq->tx_desc_dma = mp->tx_desc_sram_addr;
...@@ -1601,120 +1707,97 @@ static int txq_init(struct mv643xx_eth_private *mp, int index) ...@@ -1601,120 +1707,97 @@ static int txq_init(struct mv643xx_eth_private *mp, int index)
if (txq->tx_desc_area == NULL) { if (txq->tx_desc_area == NULL) {
dev_printk(KERN_ERR, &mp->dev->dev, dev_printk(KERN_ERR, &mp->dev->dev,
"can't allocate tx ring (%d bytes)\n", size); "can't allocate tx ring (%d bytes)\n", size);
goto out; return -ENOMEM;
} }
memset(txq->tx_desc_area, 0, size); memset(txq->tx_desc_area, 0, size);
txq->tx_desc_area_size = size; txq->tx_desc_area_size = size;
txq->tx_skb = kmalloc(txq->tx_ring_size * sizeof(*txq->tx_skb),
GFP_KERNEL);
if (txq->tx_skb == NULL) {
dev_printk(KERN_ERR, &mp->dev->dev,
"can't allocate tx skb ring\n");
goto out_free;
}
tx_desc = (struct tx_desc *)txq->tx_desc_area; tx_desc = (struct tx_desc *)txq->tx_desc_area;
for (i = 0; i < txq->tx_ring_size; i++) { for (i = 0; i < txq->tx_ring_size; i++) {
struct tx_desc *txd = tx_desc + i; struct tx_desc *txd = tx_desc + i;
int nexti = (i + 1) % txq->tx_ring_size; int nexti;
nexti = i + 1;
if (nexti == txq->tx_ring_size)
nexti = 0;
txd->cmd_sts = 0; txd->cmd_sts = 0;
txd->next_desc_ptr = txq->tx_desc_dma + txd->next_desc_ptr = txq->tx_desc_dma +
nexti * sizeof(struct tx_desc); nexti * sizeof(struct tx_desc);
} }
return 0; skb_queue_head_init(&txq->tx_skb);
out_free: return 0;
if (index == mp->txq_primary && size <= mp->tx_desc_sram_size)
iounmap(txq->tx_desc_area);
else
dma_free_coherent(NULL, size,
txq->tx_desc_area,
txq->tx_desc_dma);
out:
return -ENOMEM;
} }
static void txq_reclaim(struct tx_queue *txq, int force) static void txq_deinit(struct tx_queue *txq)
{ {
struct mv643xx_eth_private *mp = txq_to_mp(txq); struct mv643xx_eth_private *mp = txq_to_mp(txq);
unsigned long flags;
spin_lock_irqsave(&mp->lock, flags);
while (txq->tx_desc_count > 0) {
int tx_index;
struct tx_desc *desc;
u32 cmd_sts;
struct sk_buff *skb;
dma_addr_t addr;
int count;
tx_index = txq->tx_used_desc; txq_disable(txq);
desc = &txq->tx_desc_area[tx_index]; txq_reclaim(txq, txq->tx_ring_size, 1);
cmd_sts = desc->cmd_sts;
if (cmd_sts & BUFFER_OWNED_BY_DMA) { BUG_ON(txq->tx_used_desc != txq->tx_curr_desc);
if (!force)
break;
desc->cmd_sts = cmd_sts & ~BUFFER_OWNED_BY_DMA;
}
txq->tx_used_desc = (tx_index + 1) % txq->tx_ring_size; if (txq->index == 0 &&
txq->tx_desc_count--; txq->tx_desc_area_size <= mp->tx_desc_sram_size)
iounmap(txq->tx_desc_area);
else
dma_free_coherent(NULL, txq->tx_desc_area_size,
txq->tx_desc_area, txq->tx_desc_dma);
}
addr = desc->buf_ptr;
count = desc->byte_cnt;
skb = txq->tx_skb[tx_index];
txq->tx_skb[tx_index] = NULL;
if (cmd_sts & ERROR_SUMMARY) { /* netdev ops and related ***************************************************/
dev_printk(KERN_INFO, &mp->dev->dev, "tx error\n"); static int mv643xx_eth_collect_events(struct mv643xx_eth_private *mp)
mp->dev->stats.tx_errors++; {
} u32 int_cause;
u32 int_cause_ext;
/* int_cause = rdl(mp, INT_CAUSE(mp->port_num)) &
* Drop mp->lock while we free the skb. (INT_TX_END | INT_RX | INT_EXT);
*/ if (int_cause == 0)
spin_unlock_irqrestore(&mp->lock, flags); return 0;
if (cmd_sts & TX_FIRST_DESC) int_cause_ext = 0;
dma_unmap_single(NULL, addr, count, DMA_TO_DEVICE); if (int_cause & INT_EXT)
else int_cause_ext = rdl(mp, INT_CAUSE_EXT(mp->port_num));
dma_unmap_page(NULL, addr, count, DMA_TO_DEVICE);
if (skb) int_cause &= INT_TX_END | INT_RX;
dev_kfree_skb_irq(skb); if (int_cause) {
wrl(mp, INT_CAUSE(mp->port_num), ~int_cause);
mp->work_tx_end |= ((int_cause & INT_TX_END) >> 19) &
~(rdl(mp, TXQ_COMMAND(mp->port_num)) & 0xff);
mp->work_rx |= (int_cause & INT_RX) >> 2;
}
spin_lock_irqsave(&mp->lock, flags); int_cause_ext &= INT_EXT_LINK_PHY | INT_EXT_TX;
if (int_cause_ext) {
wrl(mp, INT_CAUSE_EXT(mp->port_num), ~int_cause_ext);
if (int_cause_ext & INT_EXT_LINK_PHY)
mp->work_link = 1;
mp->work_tx |= int_cause_ext & INT_EXT_TX;
} }
spin_unlock_irqrestore(&mp->lock, flags);
return 1;
} }
static void txq_deinit(struct tx_queue *txq) static irqreturn_t mv643xx_eth_irq(int irq, void *dev_id)
{ {
struct mv643xx_eth_private *mp = txq_to_mp(txq); struct net_device *dev = (struct net_device *)dev_id;
struct mv643xx_eth_private *mp = netdev_priv(dev);
txq_disable(txq);
txq_reclaim(txq, 1);
BUG_ON(txq->tx_used_desc != txq->tx_curr_desc); if (unlikely(!mv643xx_eth_collect_events(mp)))
return IRQ_NONE;
if (txq->index == mp->txq_primary && wrl(mp, INT_MASK(mp->port_num), 0);
txq->tx_desc_area_size <= mp->tx_desc_sram_size) napi_schedule(&mp->napi);
iounmap(txq->tx_desc_area);
else
dma_free_coherent(NULL, txq->tx_desc_area_size,
txq->tx_desc_area, txq->tx_desc_dma);
kfree(txq->tx_skb); return IRQ_HANDLED;
} }
/* netdev ops and related ***************************************************/
static void handle_link_event(struct mv643xx_eth_private *mp) static void handle_link_event(struct mv643xx_eth_private *mp)
{ {
struct net_device *dev = mp->dev; struct net_device *dev = mp->dev;
...@@ -1731,17 +1814,14 @@ static void handle_link_event(struct mv643xx_eth_private *mp) ...@@ -1731,17 +1814,14 @@ static void handle_link_event(struct mv643xx_eth_private *mp)
printk(KERN_INFO "%s: link down\n", dev->name); printk(KERN_INFO "%s: link down\n", dev->name);
netif_carrier_off(dev); netif_carrier_off(dev);
netif_stop_queue(dev);
for (i = 0; i < 8; i++) { for (i = 0; i < mp->txq_count; i++) {
struct tx_queue *txq = mp->txq + i; struct tx_queue *txq = mp->txq + i;
if (mp->txq_mask & (1 << i)) { txq_reclaim(txq, txq->tx_ring_size, 1);
txq_reclaim(txq, 1);
txq_reset_hw_ptr(txq); txq_reset_hw_ptr(txq);
} }
} }
}
return; return;
} }
...@@ -1767,119 +1847,93 @@ static void handle_link_event(struct mv643xx_eth_private *mp) ...@@ -1767,119 +1847,93 @@ static void handle_link_event(struct mv643xx_eth_private *mp)
speed, duplex ? "full" : "half", speed, duplex ? "full" : "half",
fc ? "en" : "dis"); fc ? "en" : "dis");
if (!netif_carrier_ok(dev)) { if (!netif_carrier_ok(dev))
netif_carrier_on(dev); netif_carrier_on(dev);
netif_wake_queue(dev);
}
} }
static irqreturn_t mv643xx_eth_irq(int irq, void *dev_id) static int mv643xx_eth_poll(struct napi_struct *napi, int budget)
{ {
struct net_device *dev = (struct net_device *)dev_id; struct mv643xx_eth_private *mp;
struct mv643xx_eth_private *mp = netdev_priv(dev); int work_done;
u32 int_cause;
u32 int_cause_ext;
int_cause = rdl(mp, INT_CAUSE(mp->port_num)) &
(INT_TX_END | INT_RX | INT_EXT);
if (int_cause == 0)
return IRQ_NONE;
int_cause_ext = 0; mp = container_of(napi, struct mv643xx_eth_private, napi);
if (int_cause & INT_EXT) {
int_cause_ext = rdl(mp, INT_CAUSE_EXT(mp->port_num))
& (INT_EXT_LINK | INT_EXT_PHY | INT_EXT_TX);
wrl(mp, INT_CAUSE_EXT(mp->port_num), ~int_cause_ext);
}
if (int_cause_ext & (INT_EXT_PHY | INT_EXT_LINK)) mp->work_rx_refill |= mp->work_rx_oom;
handle_link_event(mp); mp->work_rx_oom = 0;
/* work_done = 0;
* RxBuffer or RxError set for any of the 8 queues? while (work_done < budget) {
*/ u8 queue_mask;
#ifdef MV643XX_ETH_NAPI int queue;
if (int_cause & INT_RX) { int work_tbd;
wrl(mp, INT_CAUSE(mp->port_num), ~(int_cause & INT_RX));
wrl(mp, INT_MASK(mp->port_num), 0x00000000);
rdl(mp, INT_MASK(mp->port_num));
netif_rx_schedule(dev, &mp->napi); if (mp->work_link) {
mp->work_link = 0;
handle_link_event(mp);
continue;
} }
#else
if (int_cause & INT_RX) {
int i;
for (i = 7; i >= 0; i--) queue_mask = mp->work_tx | mp->work_tx_end |
if (mp->rxq_mask & (1 << i)) mp->work_rx | mp->work_rx_refill;
rxq_process(mp->rxq + i, INT_MAX); if (!queue_mask) {
if (mv643xx_eth_collect_events(mp))
continue;
break;
} }
#endif
/* queue = fls(queue_mask) - 1;
* TxBuffer or TxError set for any of the 8 queues? queue_mask = 1 << queue;
*/
if (int_cause_ext & INT_EXT_TX) {
int i;
for (i = 0; i < 8; i++) work_tbd = budget - work_done;
if (mp->txq_mask & (1 << i)) if (work_tbd > 16)
txq_reclaim(mp->txq + i, 0); work_tbd = 16;
/* if (mp->work_tx_end & queue_mask) {
* Enough space again in the primary TX queue for a txq_kick(mp->txq + queue);
* full packet? } else if (mp->work_tx & queue_mask) {
*/ work_done += txq_reclaim(mp->txq + queue, work_tbd, 0);
if (netif_carrier_ok(dev)) { txq_maybe_wake(mp->txq + queue);
spin_lock(&mp->lock); } else if (mp->work_rx & queue_mask) {
__txq_maybe_wake(mp->txq + mp->txq_primary); work_done += rxq_process(mp->rxq + queue, work_tbd);
spin_unlock(&mp->lock); } else if (mp->work_rx_refill & queue_mask) {
work_done += rxq_refill(mp->rxq + queue, work_tbd);
} else {
BUG();
} }
} }
/* if (work_done < budget) {
* Any TxEnd interrupts? if (mp->work_rx_oom)
*/ mod_timer(&mp->rx_oom, jiffies + (HZ / 10));
if (int_cause & INT_TX_END) { napi_complete(napi);
int i; wrl(mp, INT_MASK(mp->port_num), INT_TX_END | INT_RX | INT_EXT);
}
wrl(mp, INT_CAUSE(mp->port_num), ~(int_cause & INT_TX_END));
spin_lock(&mp->lock);
for (i = 0; i < 8; i++) {
struct tx_queue *txq = mp->txq + i;
u32 hw_desc_ptr;
u32 expected_ptr;
if ((int_cause & (INT_TX_END_0 << i)) == 0)
continue;
hw_desc_ptr = return work_done;
rdl(mp, TXQ_CURRENT_DESC_PTR(mp->port_num, i)); }
expected_ptr = (u32)txq->tx_desc_dma +
txq->tx_curr_desc * sizeof(struct tx_desc);
if (hw_desc_ptr != expected_ptr) static inline void oom_timer_wrapper(unsigned long data)
txq_enable(txq); {
} struct mv643xx_eth_private *mp = (void *)data;
spin_unlock(&mp->lock);
}
return IRQ_HANDLED; napi_schedule(&mp->napi);
} }
static void phy_reset(struct mv643xx_eth_private *mp) static void phy_reset(struct mv643xx_eth_private *mp)
{ {
unsigned int data; int data;
data = phy_read(mp->phy, MII_BMCR);
if (data < 0)
return;
smi_reg_read(mp, mp->phy_addr, MII_BMCR, &data);
data |= BMCR_RESET; data |= BMCR_RESET;
smi_reg_write(mp, mp->phy_addr, MII_BMCR, data); if (phy_write(mp->phy, MII_BMCR, data) < 0)
return;
do { do {
udelay(1); data = phy_read(mp->phy, MII_BMCR);
smi_reg_read(mp, mp->phy_addr, MII_BMCR, &data); } while (data >= 0 && data & BMCR_RESET);
} while (data & BMCR_RESET);
} }
static void port_start(struct mv643xx_eth_private *mp) static void port_start(struct mv643xx_eth_private *mp)
...@@ -1890,7 +1944,7 @@ static void port_start(struct mv643xx_eth_private *mp) ...@@ -1890,7 +1944,7 @@ static void port_start(struct mv643xx_eth_private *mp)
/* /*
* Perform PHY reset, if there is a PHY. * Perform PHY reset, if there is a PHY.
*/ */
if (mp->phy_addr != -1) { if (mp->phy != NULL) {
struct ethtool_cmd cmd; struct ethtool_cmd cmd;
mv643xx_eth_get_settings(mp->dev, &cmd); mv643xx_eth_get_settings(mp->dev, &cmd);
...@@ -1907,7 +1961,7 @@ static void port_start(struct mv643xx_eth_private *mp) ...@@ -1907,7 +1961,7 @@ static void port_start(struct mv643xx_eth_private *mp)
wrl(mp, PORT_SERIAL_CONTROL(mp->port_num), pscr); wrl(mp, PORT_SERIAL_CONTROL(mp->port_num), pscr);
pscr |= DO_NOT_FORCE_LINK_FAIL; pscr |= DO_NOT_FORCE_LINK_FAIL;
if (mp->phy_addr == -1) if (mp->phy == NULL)
pscr |= FORCE_LINK_PASS; pscr |= FORCE_LINK_PASS;
wrl(mp, PORT_SERIAL_CONTROL(mp->port_num), pscr); wrl(mp, PORT_SERIAL_CONTROL(mp->port_num), pscr);
...@@ -1917,12 +1971,9 @@ static void port_start(struct mv643xx_eth_private *mp) ...@@ -1917,12 +1971,9 @@ static void port_start(struct mv643xx_eth_private *mp)
* Configure TX path and queues. * Configure TX path and queues.
*/ */
tx_set_rate(mp, 1000000000, 16777216); tx_set_rate(mp, 1000000000, 16777216);
for (i = 0; i < 8; i++) { for (i = 0; i < mp->txq_count; i++) {
struct tx_queue *txq = mp->txq + i; struct tx_queue *txq = mp->txq + i;
if ((mp->txq_mask & (1 << i)) == 0)
continue;
txq_reset_hw_ptr(txq); txq_reset_hw_ptr(txq);
txq_set_rate(txq, 1000000000, 16777216); txq_set_rate(txq, 1000000000, 16777216);
txq_set_fixed_prio_mode(txq); txq_set_fixed_prio_mode(txq);
...@@ -1935,9 +1986,10 @@ static void port_start(struct mv643xx_eth_private *mp) ...@@ -1935,9 +1986,10 @@ static void port_start(struct mv643xx_eth_private *mp)
/* /*
* Receive all unmatched unicast, TCP, UDP, BPDU and broadcast * Receive all unmatched unicast, TCP, UDP, BPDU and broadcast
* frames to RX queue #0. * frames to RX queue #0, and include the pseudo-header when
* calculating receive checksums.
*/ */
wrl(mp, PORT_CONFIG(mp->port_num), 0x00000000); wrl(mp, PORT_CONFIG(mp->port_num), 0x02000000);
/* /*
* Treat BPDUs as normal multicasts, and disable partition mode. * Treat BPDUs as normal multicasts, and disable partition mode.
...@@ -1947,14 +1999,11 @@ static void port_start(struct mv643xx_eth_private *mp) ...@@ -1947,14 +1999,11 @@ static void port_start(struct mv643xx_eth_private *mp)
/* /*
* Enable the receive queues. * Enable the receive queues.
*/ */
for (i = 0; i < 8; i++) { for (i = 0; i < mp->rxq_count; i++) {
struct rx_queue *rxq = mp->rxq + i; struct rx_queue *rxq = mp->rxq + i;
int off = RXQ_CURRENT_DESC_PTR(mp->port_num, i); int off = RXQ_CURRENT_DESC_PTR(mp->port_num, i);
u32 addr; u32 addr;
if ((mp->rxq_mask & (1 << i)) == 0)
continue;
addr = (u32)rxq->rx_desc_dma; addr = (u32)rxq->rx_desc_dma;
addr += rxq->rx_curr_desc * sizeof(struct rx_desc); addr += rxq->rx_curr_desc * sizeof(struct rx_desc);
wrl(mp, off, addr); wrl(mp, off, addr);
...@@ -2004,8 +2053,7 @@ static int mv643xx_eth_open(struct net_device *dev) ...@@ -2004,8 +2053,7 @@ static int mv643xx_eth_open(struct net_device *dev)
rdl(mp, INT_CAUSE_EXT(mp->port_num)); rdl(mp, INT_CAUSE_EXT(mp->port_num));
err = request_irq(dev->irq, mv643xx_eth_irq, err = request_irq(dev->irq, mv643xx_eth_irq,
IRQF_SHARED | IRQF_SAMPLE_RANDOM, IRQF_SHARED, dev->name, dev);
dev->name, dev);
if (err) { if (err) {
dev_printk(KERN_ERR, &dev->dev, "can't assign irq\n"); dev_printk(KERN_ERR, &dev->dev, "can't assign irq\n");
return -EAGAIN; return -EAGAIN;
...@@ -2013,57 +2061,48 @@ static int mv643xx_eth_open(struct net_device *dev) ...@@ -2013,57 +2061,48 @@ static int mv643xx_eth_open(struct net_device *dev)
init_mac_tables(mp); init_mac_tables(mp);
for (i = 0; i < 8; i++) { napi_enable(&mp->napi);
if ((mp->rxq_mask & (1 << i)) == 0)
continue;
for (i = 0; i < mp->rxq_count; i++) {
err = rxq_init(mp, i); err = rxq_init(mp, i);
if (err) { if (err) {
while (--i >= 0) while (--i >= 0)
if (mp->rxq_mask & (1 << i))
rxq_deinit(mp->rxq + i); rxq_deinit(mp->rxq + i);
goto out; goto out;
} }
rxq_refill(mp->rxq + i); rxq_refill(mp->rxq + i, INT_MAX);
} }
for (i = 0; i < 8; i++) { if (mp->work_rx_oom) {
if ((mp->txq_mask & (1 << i)) == 0) mp->rx_oom.expires = jiffies + (HZ / 10);
continue; add_timer(&mp->rx_oom);
}
for (i = 0; i < mp->txq_count; i++) {
err = txq_init(mp, i); err = txq_init(mp, i);
if (err) { if (err) {
while (--i >= 0) while (--i >= 0)
if (mp->txq_mask & (1 << i))
txq_deinit(mp->txq + i); txq_deinit(mp->txq + i);
goto out_free; goto out_free;
} }
} }
#ifdef MV643XX_ETH_NAPI
napi_enable(&mp->napi);
#endif
netif_carrier_off(dev); netif_carrier_off(dev);
netif_stop_queue(dev);
port_start(mp); port_start(mp);
set_rx_coal(mp, 0); set_rx_coal(mp, 0);
set_tx_coal(mp, 0); set_tx_coal(mp, 0);
wrl(mp, INT_MASK_EXT(mp->port_num), wrl(mp, INT_MASK_EXT(mp->port_num), INT_EXT_LINK_PHY | INT_EXT_TX);
INT_EXT_LINK | INT_EXT_PHY | INT_EXT_TX);
wrl(mp, INT_MASK(mp->port_num), INT_TX_END | INT_RX | INT_EXT); wrl(mp, INT_MASK(mp->port_num), INT_TX_END | INT_RX | INT_EXT);
return 0; return 0;
out_free: out_free:
for (i = 0; i < 8; i++) for (i = 0; i < mp->rxq_count; i++)
if (mp->rxq_mask & (1 << i))
rxq_deinit(mp->rxq + i); rxq_deinit(mp->rxq + i);
out: out:
free_irq(dev->irq, dev); free_irq(dev->irq, dev);
...@@ -2076,12 +2115,10 @@ static void port_reset(struct mv643xx_eth_private *mp) ...@@ -2076,12 +2115,10 @@ static void port_reset(struct mv643xx_eth_private *mp)
unsigned int data; unsigned int data;
int i; int i;
for (i = 0; i < 8; i++) { for (i = 0; i < mp->rxq_count; i++)
if (mp->rxq_mask & (1 << i))
rxq_disable(mp->rxq + i); rxq_disable(mp->rxq + i);
if (mp->txq_mask & (1 << i)) for (i = 0; i < mp->txq_count; i++)
txq_disable(mp->txq + i); txq_disable(mp->txq + i);
}
while (1) { while (1) {
u32 ps = rdl(mp, PORT_STATUS(mp->port_num)); u32 ps = rdl(mp, PORT_STATUS(mp->port_num));
...@@ -2107,23 +2144,24 @@ static int mv643xx_eth_stop(struct net_device *dev) ...@@ -2107,23 +2144,24 @@ static int mv643xx_eth_stop(struct net_device *dev)
wrl(mp, INT_MASK(mp->port_num), 0x00000000); wrl(mp, INT_MASK(mp->port_num), 0x00000000);
rdl(mp, INT_MASK(mp->port_num)); rdl(mp, INT_MASK(mp->port_num));
#ifdef MV643XX_ETH_NAPI del_timer_sync(&mp->mib_counters_timer);
napi_disable(&mp->napi); napi_disable(&mp->napi);
#endif
del_timer_sync(&mp->rx_oom);
netif_carrier_off(dev); netif_carrier_off(dev);
netif_stop_queue(dev);
free_irq(dev->irq, dev); free_irq(dev->irq, dev);
port_reset(mp); port_reset(mp);
mv643xx_eth_get_stats(dev);
mib_counters_update(mp); mib_counters_update(mp);
for (i = 0; i < 8; i++) { for (i = 0; i < mp->rxq_count; i++)
if (mp->rxq_mask & (1 << i))
rxq_deinit(mp->rxq + i); rxq_deinit(mp->rxq + i);
if (mp->txq_mask & (1 << i)) for (i = 0; i < mp->txq_count; i++)
txq_deinit(mp->txq + i); txq_deinit(mp->txq + i);
}
return 0; return 0;
} }
...@@ -2132,8 +2170,8 @@ static int mv643xx_eth_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) ...@@ -2132,8 +2170,8 @@ static int mv643xx_eth_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
{ {
struct mv643xx_eth_private *mp = netdev_priv(dev); struct mv643xx_eth_private *mp = netdev_priv(dev);
if (mp->phy_addr != -1) if (mp->phy != NULL)
return generic_mii_ioctl(&mp->mii, if_mii(ifr), cmd, NULL); return phy_mii_ioctl(mp->phy, if_mii(ifr), cmd);
return -EOPNOTSUPP; return -EOPNOTSUPP;
} }
...@@ -2173,12 +2211,10 @@ static void tx_timeout_task(struct work_struct *ugly) ...@@ -2173,12 +2211,10 @@ static void tx_timeout_task(struct work_struct *ugly)
mp = container_of(ugly, struct mv643xx_eth_private, tx_timeout_task); mp = container_of(ugly, struct mv643xx_eth_private, tx_timeout_task);
if (netif_running(mp->dev)) { if (netif_running(mp->dev)) {
netif_stop_queue(mp->dev); netif_tx_stop_all_queues(mp->dev);
port_reset(mp); port_reset(mp);
port_start(mp); port_start(mp);
netif_tx_wake_all_queues(mp->dev);
__txq_maybe_wake(mp->txq + mp->txq_primary);
} }
} }
...@@ -2205,22 +2241,6 @@ static void mv643xx_eth_netpoll(struct net_device *dev) ...@@ -2205,22 +2241,6 @@ static void mv643xx_eth_netpoll(struct net_device *dev)
} }
#endif #endif
static int mv643xx_eth_mdio_read(struct net_device *dev, int addr, int reg)
{
struct mv643xx_eth_private *mp = netdev_priv(dev);
int val;
smi_reg_read(mp, addr, reg, &val);
return val;
}
static void mv643xx_eth_mdio_write(struct net_device *dev, int addr, int reg, int val)
{
struct mv643xx_eth_private *mp = netdev_priv(dev);
smi_reg_write(mp, addr, reg, val);
}
/* platform glue ************************************************************/ /* platform glue ************************************************************/
static void static void
...@@ -2272,14 +2292,20 @@ static void infer_hw_params(struct mv643xx_eth_shared_private *msp) ...@@ -2272,14 +2292,20 @@ static void infer_hw_params(struct mv643xx_eth_shared_private *msp)
msp->extended_rx_coal_limit = 0; msp->extended_rx_coal_limit = 0;
/* /*
* Check whether the TX rate control registers are in the * Check whether the MAC supports TX rate control, and if
* old or the new place. * yes, whether its associated registers are in the old or
* the new place.
*/ */
writel(1, msp->base + TX_BW_MTU_MOVED(0)); writel(1, msp->base + TX_BW_MTU_MOVED(0));
if (readl(msp->base + TX_BW_MTU_MOVED(0)) & 1) if (readl(msp->base + TX_BW_MTU_MOVED(0)) & 1) {
msp->tx_bw_control_moved = 1; msp->tx_bw_control = TX_BW_CONTROL_NEW_LAYOUT;
} else {
writel(7, msp->base + TX_BW_RATE(0));
if (readl(msp->base + TX_BW_RATE(0)) & 7)
msp->tx_bw_control = TX_BW_CONTROL_OLD_LAYOUT;
else else
msp->tx_bw_control_moved = 0; msp->tx_bw_control = TX_BW_CONTROL_ABSENT;
}
} }
static int mv643xx_eth_shared_probe(struct platform_device *pdev) static int mv643xx_eth_shared_probe(struct platform_device *pdev)
...@@ -2309,7 +2335,41 @@ static int mv643xx_eth_shared_probe(struct platform_device *pdev) ...@@ -2309,7 +2335,41 @@ static int mv643xx_eth_shared_probe(struct platform_device *pdev)
if (msp->base == NULL) if (msp->base == NULL)
goto out_free; goto out_free;
spin_lock_init(&msp->phy_lock); /*
* Set up and register SMI bus.
*/
if (pd == NULL || pd->shared_smi == NULL) {
msp->smi_bus.priv = msp;
msp->smi_bus.name = "mv643xx_eth smi";
msp->smi_bus.read = smi_bus_read;
msp->smi_bus.write = smi_bus_write,
snprintf(msp->smi_bus.id, MII_BUS_ID_SIZE, "%d", pdev->id);
msp->smi_bus.dev = &pdev->dev;
msp->smi_bus.phy_mask = 0xffffffff;
if (mdiobus_register(&msp->smi_bus) < 0)
goto out_unmap;
msp->smi = msp;
} else {
msp->smi = platform_get_drvdata(pd->shared_smi);
}
msp->err_interrupt = NO_IRQ;
init_waitqueue_head(&msp->smi_busy_wait);
/*
* Check whether the error interrupt is hooked up.
*/
res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
if (res != NULL) {
int err;
err = request_irq(res->start, mv643xx_eth_err_irq,
IRQF_SHARED, "mv643xx_eth", msp);
if (!err) {
writel(ERR_INT_SMI_DONE, msp->base + ERR_INT_MASK);
msp->err_interrupt = res->start;
}
}
/* /*
* (Re-)program MBUS remapping windows if we are asked to. * (Re-)program MBUS remapping windows if we are asked to.
...@@ -2327,6 +2387,8 @@ static int mv643xx_eth_shared_probe(struct platform_device *pdev) ...@@ -2327,6 +2387,8 @@ static int mv643xx_eth_shared_probe(struct platform_device *pdev)
return 0; return 0;
out_unmap:
iounmap(msp->base);
out_free: out_free:
kfree(msp); kfree(msp);
out: out:
...@@ -2336,7 +2398,12 @@ out: ...@@ -2336,7 +2398,12 @@ out:
static int mv643xx_eth_shared_remove(struct platform_device *pdev) static int mv643xx_eth_shared_remove(struct platform_device *pdev)
{ {
struct mv643xx_eth_shared_private *msp = platform_get_drvdata(pdev); struct mv643xx_eth_shared_private *msp = platform_get_drvdata(pdev);
struct mv643xx_eth_shared_platform_data *pd = pdev->dev.platform_data;
if (pd == NULL || pd->shared_smi == NULL)
mdiobus_unregister(&msp->smi_bus);
if (msp->err_interrupt != NO_IRQ)
free_irq(msp->err_interrupt, msp);
iounmap(msp->base); iounmap(msp->base);
kfree(msp); kfree(msp);
...@@ -2382,33 +2449,13 @@ static void set_params(struct mv643xx_eth_private *mp, ...@@ -2382,33 +2449,13 @@ static void set_params(struct mv643xx_eth_private *mp,
else else
uc_addr_get(mp, dev->dev_addr); uc_addr_get(mp, dev->dev_addr);
if (pd->phy_addr == -1) {
mp->shared_smi = NULL;
mp->phy_addr = -1;
} else {
mp->shared_smi = mp->shared;
if (pd->shared_smi != NULL)
mp->shared_smi = platform_get_drvdata(pd->shared_smi);
if (pd->force_phy_addr || pd->phy_addr) {
mp->phy_addr = pd->phy_addr & 0x3f;
phy_addr_set(mp, mp->phy_addr);
} else {
mp->phy_addr = phy_addr_get(mp);
}
}
mp->default_rx_ring_size = DEFAULT_RX_QUEUE_SIZE; mp->default_rx_ring_size = DEFAULT_RX_QUEUE_SIZE;
if (pd->rx_queue_size) if (pd->rx_queue_size)
mp->default_rx_ring_size = pd->rx_queue_size; mp->default_rx_ring_size = pd->rx_queue_size;
mp->rx_desc_sram_addr = pd->rx_sram_addr; mp->rx_desc_sram_addr = pd->rx_sram_addr;
mp->rx_desc_sram_size = pd->rx_sram_size; mp->rx_desc_sram_size = pd->rx_sram_size;
if (pd->rx_queue_mask) mp->rxq_count = pd->rx_queue_count ? : 1;
mp->rxq_mask = pd->rx_queue_mask;
else
mp->rxq_mask = 0x01;
mp->rxq_primary = fls(mp->rxq_mask) - 1;
mp->default_tx_ring_size = DEFAULT_TX_QUEUE_SIZE; mp->default_tx_ring_size = DEFAULT_TX_QUEUE_SIZE;
if (pd->tx_queue_size) if (pd->tx_queue_size)
...@@ -2416,76 +2463,63 @@ static void set_params(struct mv643xx_eth_private *mp, ...@@ -2416,76 +2463,63 @@ static void set_params(struct mv643xx_eth_private *mp,
mp->tx_desc_sram_addr = pd->tx_sram_addr; mp->tx_desc_sram_addr = pd->tx_sram_addr;
mp->tx_desc_sram_size = pd->tx_sram_size; mp->tx_desc_sram_size = pd->tx_sram_size;
if (pd->tx_queue_mask) mp->txq_count = pd->tx_queue_count ? : 1;
mp->txq_mask = pd->tx_queue_mask;
else
mp->txq_mask = 0x01;
mp->txq_primary = fls(mp->txq_mask) - 1;
} }
static int phy_detect(struct mv643xx_eth_private *mp) static struct phy_device *phy_scan(struct mv643xx_eth_private *mp,
int phy_addr)
{ {
unsigned int data; struct mii_bus *bus = &mp->shared->smi->smi_bus;
unsigned int data2; struct phy_device *phydev;
int start;
int num;
int i;
if (phy_addr == MV643XX_ETH_PHY_ADDR_DEFAULT) {
start = phy_addr_get(mp) & 0x1f;
num = 32;
} else {
start = phy_addr & 0x1f;
num = 1;
}
smi_reg_read(mp, mp->phy_addr, MII_BMCR, &data); phydev = NULL;
smi_reg_write(mp, mp->phy_addr, MII_BMCR, data ^ BMCR_ANENABLE); for (i = 0; i < num; i++) {
int addr = (start + i) & 0x1f;
smi_reg_read(mp, mp->phy_addr, MII_BMCR, &data2); if (bus->phy_map[addr] == NULL)
if (((data ^ data2) & BMCR_ANENABLE) == 0) mdiobus_scan(bus, addr);
return -ENODEV;
smi_reg_write(mp, mp->phy_addr, MII_BMCR, data); if (phydev == NULL) {
phydev = bus->phy_map[addr];
if (phydev != NULL)
phy_addr_set(mp, addr);
}
}
return 0; return phydev;
} }
static int phy_init(struct mv643xx_eth_private *mp, static void phy_init(struct mv643xx_eth_private *mp, int speed, int duplex)
struct mv643xx_eth_platform_data *pd)
{ {
struct ethtool_cmd cmd; struct phy_device *phy = mp->phy;
int err;
err = phy_detect(mp);
if (err) {
dev_printk(KERN_INFO, &mp->dev->dev,
"no PHY detected at addr %d\n", mp->phy_addr);
return err;
}
phy_reset(mp); phy_reset(mp);
mp->mii.phy_id = mp->phy_addr; phy_attach(mp->dev, phy->dev.bus_id, 0, PHY_INTERFACE_MODE_GMII);
mp->mii.phy_id_mask = 0x3f;
mp->mii.reg_num_mask = 0x1f; if (speed == 0) {
mp->mii.dev = mp->dev; phy->autoneg = AUTONEG_ENABLE;
mp->mii.mdio_read = mv643xx_eth_mdio_read; phy->speed = 0;
mp->mii.mdio_write = mv643xx_eth_mdio_write; phy->duplex = 0;
phy->advertising = phy->supported | ADVERTISED_Autoneg;
mp->mii.supports_gmii = mii_check_gmii_support(&mp->mii);
memset(&cmd, 0, sizeof(cmd));
cmd.port = PORT_MII;
cmd.transceiver = XCVR_INTERNAL;
cmd.phy_address = mp->phy_addr;
if (pd->speed == 0) {
cmd.autoneg = AUTONEG_ENABLE;
cmd.speed = SPEED_100;
cmd.advertising = ADVERTISED_10baseT_Half |
ADVERTISED_10baseT_Full |
ADVERTISED_100baseT_Half |
ADVERTISED_100baseT_Full;
if (mp->mii.supports_gmii)
cmd.advertising |= ADVERTISED_1000baseT_Full;
} else { } else {
cmd.autoneg = AUTONEG_DISABLE; phy->autoneg = AUTONEG_DISABLE;
cmd.speed = pd->speed; phy->advertising = 0;
cmd.duplex = pd->duplex; phy->speed = speed;
phy->duplex = duplex;
} }
phy_start_aneg(phy);
mv643xx_eth_set_settings(mp->dev, &cmd);
return 0;
} }
static void init_pscr(struct mv643xx_eth_private *mp, int speed, int duplex) static void init_pscr(struct mv643xx_eth_private *mp, int speed, int duplex)
...@@ -2499,7 +2533,7 @@ static void init_pscr(struct mv643xx_eth_private *mp, int speed, int duplex) ...@@ -2499,7 +2533,7 @@ static void init_pscr(struct mv643xx_eth_private *mp, int speed, int duplex)
} }
pscr = MAX_RX_PACKET_9700BYTE | SERIAL_PORT_CONTROL_RESERVED; pscr = MAX_RX_PACKET_9700BYTE | SERIAL_PORT_CONTROL_RESERVED;
if (mp->phy_addr == -1) { if (mp->phy == NULL) {
pscr |= DISABLE_AUTO_NEG_SPEED_GMII; pscr |= DISABLE_AUTO_NEG_SPEED_GMII;
if (speed == SPEED_1000) if (speed == SPEED_1000)
pscr |= SET_GMII_SPEED_TO_1000; pscr |= SET_GMII_SPEED_TO_1000;
...@@ -2538,7 +2572,7 @@ static int mv643xx_eth_probe(struct platform_device *pdev) ...@@ -2538,7 +2572,7 @@ static int mv643xx_eth_probe(struct platform_device *pdev)
return -ENODEV; return -ENODEV;
} }
dev = alloc_etherdev(sizeof(struct mv643xx_eth_private)); dev = alloc_etherdev_mq(sizeof(struct mv643xx_eth_private), 8);
if (!dev) if (!dev)
return -ENOMEM; return -ENOMEM;
...@@ -2549,33 +2583,47 @@ static int mv643xx_eth_probe(struct platform_device *pdev) ...@@ -2549,33 +2583,47 @@ static int mv643xx_eth_probe(struct platform_device *pdev)
mp->port_num = pd->port_number; mp->port_num = pd->port_number;
mp->dev = dev; mp->dev = dev;
#ifdef MV643XX_ETH_NAPI
netif_napi_add(dev, &mp->napi, mv643xx_eth_poll, 64);
#endif
set_params(mp, pd); set_params(mp, pd);
dev->real_num_tx_queues = mp->txq_count;
spin_lock_init(&mp->lock); if (pd->phy_addr != MV643XX_ETH_PHY_NONE)
mp->phy = phy_scan(mp, pd->phy_addr);
mib_counters_clear(mp);
INIT_WORK(&mp->tx_timeout_task, tx_timeout_task);
if (mp->phy_addr != -1) {
err = phy_init(mp, pd);
if (err)
goto out;
if (mp->phy != NULL) {
phy_init(mp, pd->speed, pd->duplex);
SET_ETHTOOL_OPS(dev, &mv643xx_eth_ethtool_ops); SET_ETHTOOL_OPS(dev, &mv643xx_eth_ethtool_ops);
} else { } else {
SET_ETHTOOL_OPS(dev, &mv643xx_eth_ethtool_ops_phyless); SET_ETHTOOL_OPS(dev, &mv643xx_eth_ethtool_ops_phyless);
} }
init_pscr(mp, pd->speed, pd->duplex); init_pscr(mp, pd->speed, pd->duplex);
mib_counters_clear(mp);
init_timer(&mp->mib_counters_timer);
mp->mib_counters_timer.data = (unsigned long)mp;
mp->mib_counters_timer.function = mib_counters_timer_wrapper;
mp->mib_counters_timer.expires = jiffies + 30 * HZ;
add_timer(&mp->mib_counters_timer);
spin_lock_init(&mp->mib_counters_lock);
INIT_WORK(&mp->tx_timeout_task, tx_timeout_task);
netif_napi_add(dev, &mp->napi, mv643xx_eth_poll, 128);
init_timer(&mp->rx_oom);
mp->rx_oom.data = (unsigned long)mp;
mp->rx_oom.function = oom_timer_wrapper;
res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
BUG_ON(!res); BUG_ON(!res);
dev->irq = res->start; dev->irq = res->start;
dev->get_stats = mv643xx_eth_get_stats;
dev->hard_start_xmit = mv643xx_eth_xmit; dev->hard_start_xmit = mv643xx_eth_xmit;
dev->open = mv643xx_eth_open; dev->open = mv643xx_eth_open;
dev->stop = mv643xx_eth_stop; dev->stop = mv643xx_eth_stop;
...@@ -2590,14 +2638,8 @@ static int mv643xx_eth_probe(struct platform_device *pdev) ...@@ -2590,14 +2638,8 @@ static int mv643xx_eth_probe(struct platform_device *pdev)
dev->watchdog_timeo = 2 * HZ; dev->watchdog_timeo = 2 * HZ;
dev->base_addr = 0; dev->base_addr = 0;
#ifdef MV643XX_ETH_CHECKSUM_OFFLOAD_TX
/*
* Zero copy can only work if we use Discovery II memory. Else, we will
* have to map the buffers to ISA memory which is only 16 MB
*/
dev->features = NETIF_F_SG | NETIF_F_IP_CSUM; dev->features = NETIF_F_SG | NETIF_F_IP_CSUM;
dev->vlan_features = NETIF_F_SG | NETIF_F_IP_CSUM; dev->vlan_features = NETIF_F_SG | NETIF_F_IP_CSUM;
#endif
SET_NETDEV_DEV(dev, &pdev->dev); SET_NETDEV_DEV(dev, &pdev->dev);
...@@ -2611,16 +2653,6 @@ static int mv643xx_eth_probe(struct platform_device *pdev) ...@@ -2611,16 +2653,6 @@ static int mv643xx_eth_probe(struct platform_device *pdev)
dev_printk(KERN_NOTICE, &dev->dev, "port %d with MAC address %s\n", dev_printk(KERN_NOTICE, &dev->dev, "port %d with MAC address %s\n",
mp->port_num, print_mac(mac, dev->dev_addr)); mp->port_num, print_mac(mac, dev->dev_addr));
if (dev->features & NETIF_F_SG)
dev_printk(KERN_NOTICE, &dev->dev, "scatter/gather enabled\n");
if (dev->features & NETIF_F_IP_CSUM)
dev_printk(KERN_NOTICE, &dev->dev, "tx checksum offload\n");
#ifdef MV643XX_ETH_NAPI
dev_printk(KERN_NOTICE, &dev->dev, "napi enabled\n");
#endif
if (mp->tx_desc_sram_size > 0) if (mp->tx_desc_sram_size > 0)
dev_printk(KERN_NOTICE, &dev->dev, "configured with sram\n"); dev_printk(KERN_NOTICE, &dev->dev, "configured with sram\n");
...@@ -2637,6 +2669,8 @@ static int mv643xx_eth_remove(struct platform_device *pdev) ...@@ -2637,6 +2669,8 @@ static int mv643xx_eth_remove(struct platform_device *pdev)
struct mv643xx_eth_private *mp = platform_get_drvdata(pdev); struct mv643xx_eth_private *mp = platform_get_drvdata(pdev);
unregister_netdev(mp->dev); unregister_netdev(mp->dev);
if (mp->phy != NULL)
phy_detach(mp->phy);
flush_scheduled_work(); flush_scheduled_work();
free_netdev(mp->dev); free_netdev(mp->dev);
......
...@@ -60,17 +60,41 @@ int mdiobus_register(struct mii_bus *bus) ...@@ -60,17 +60,41 @@ int mdiobus_register(struct mii_bus *bus)
bus->reset(bus); bus->reset(bus);
for (i = 0; i < PHY_MAX_ADDR; i++) { for (i = 0; i < PHY_MAX_ADDR; i++) {
bus->phy_map[i] = NULL;
if ((bus->phy_mask & (1 << i)) == 0) {
struct phy_device *phydev; struct phy_device *phydev;
if (bus->phy_mask & (1 << i)) { phydev = mdiobus_scan(bus, i);
bus->phy_map[i] = NULL; if (IS_ERR(phydev))
continue; err = PTR_ERR(phydev);
}
} }
phydev = get_phy_device(bus, i); pr_info("%s: probed\n", bus->name);
if (IS_ERR(phydev)) return err;
return PTR_ERR(phydev); }
EXPORT_SYMBOL(mdiobus_register);
void mdiobus_unregister(struct mii_bus *bus)
{
int i;
for (i = 0; i < PHY_MAX_ADDR; i++) {
if (bus->phy_map[i])
device_unregister(&bus->phy_map[i]->dev);
}
}
EXPORT_SYMBOL(mdiobus_unregister);
struct phy_device *mdiobus_scan(struct mii_bus *bus, int addr)
{
struct phy_device *phydev;
int err;
phydev = get_phy_device(bus, addr);
if (IS_ERR(phydev) || phydev == NULL)
return phydev;
/* There's a PHY at this address /* There's a PHY at this address
* We need to set: * We need to set:
...@@ -80,12 +104,12 @@ int mdiobus_register(struct mii_bus *bus) ...@@ -80,12 +104,12 @@ int mdiobus_register(struct mii_bus *bus)
* 4) bus * 4) bus
* 5) mii_bus * 5) mii_bus
* And, we need to register it */ * And, we need to register it */
if (phydev) {
phydev->irq = bus->irq[i]; phydev->irq = bus->irq != NULL ? bus->irq[addr] : PHY_POLL;
phydev->dev.parent = bus->dev; phydev->dev.parent = bus->dev;
phydev->dev.bus = &mdio_bus_type; phydev->dev.bus = &mdio_bus_type;
snprintf(phydev->dev.bus_id, BUS_ID_SIZE, PHY_ID_FMT, bus->id, i); snprintf(phydev->dev.bus_id, BUS_ID_SIZE, PHY_ID_FMT, bus->id, addr);
phydev->bus = bus; phydev->bus = bus;
...@@ -93,34 +117,17 @@ int mdiobus_register(struct mii_bus *bus) ...@@ -93,34 +117,17 @@ int mdiobus_register(struct mii_bus *bus)
phy_scan_fixups(phydev); phy_scan_fixups(phydev);
err = device_register(&phydev->dev); err = device_register(&phydev->dev);
if (err) { if (err) {
printk(KERN_ERR "phy %d failed to register\n", printk(KERN_ERR "phy %d failed to register\n", addr);
i);
phy_device_free(phydev); phy_device_free(phydev);
phydev = NULL; phydev = NULL;
} }
}
bus->phy_map[i] = phydev; bus->phy_map[addr] = phydev;
}
pr_info("%s: probed\n", bus->name); return phydev;
return err;
} }
EXPORT_SYMBOL(mdiobus_register); EXPORT_SYMBOL(mdiobus_scan);
void mdiobus_unregister(struct mii_bus *bus)
{
int i;
for (i = 0; i < PHY_MAX_ADDR; i++) {
if (bus->phy_map[i])
device_unregister(&bus->phy_map[i]->dev);
}
}
EXPORT_SYMBOL(mdiobus_unregister);
/** /**
* mdio_bus_match - determine if given PHY driver supports the given PHY device * mdio_bus_match - determine if given PHY driver supports the given PHY device
......
...@@ -17,9 +17,14 @@ ...@@ -17,9 +17,14 @@
struct mv643xx_eth_shared_platform_data { struct mv643xx_eth_shared_platform_data {
struct mbus_dram_target_info *dram; struct mbus_dram_target_info *dram;
struct platform_device *shared_smi;
unsigned int t_clk; unsigned int t_clk;
}; };
#define MV643XX_ETH_PHY_ADDR_DEFAULT 0
#define MV643XX_ETH_PHY_ADDR(x) (0x80 | (x))
#define MV643XX_ETH_PHY_NONE 0xff
struct mv643xx_eth_platform_data { struct mv643xx_eth_platform_data {
/* /*
* Pointer back to our parent instance, and our port number. * Pointer back to our parent instance, and our port number.
...@@ -30,8 +35,6 @@ struct mv643xx_eth_platform_data { ...@@ -30,8 +35,6 @@ struct mv643xx_eth_platform_data {
/* /*
* Whether a PHY is present, and if yes, at which address. * Whether a PHY is present, and if yes, at which address.
*/ */
struct platform_device *shared_smi;
int force_phy_addr;
int phy_addr; int phy_addr;
/* /*
...@@ -49,10 +52,10 @@ struct mv643xx_eth_platform_data { ...@@ -49,10 +52,10 @@ struct mv643xx_eth_platform_data {
int duplex; int duplex;
/* /*
* Which RX/TX queues to use. * How many RX/TX queues to use.
*/ */
int rx_queue_mask; int rx_queue_count;
int tx_queue_mask; int tx_queue_count;
/* /*
* Override default RX/TX queue sizes if nonzero. * Override default RX/TX queue sizes if nonzero.
......
...@@ -410,6 +410,8 @@ int phy_start_aneg(struct phy_device *phydev); ...@@ -410,6 +410,8 @@ int phy_start_aneg(struct phy_device *phydev);
int mdiobus_register(struct mii_bus *bus); int mdiobus_register(struct mii_bus *bus);
void mdiobus_unregister(struct mii_bus *bus); void mdiobus_unregister(struct mii_bus *bus);
struct phy_device *mdiobus_scan(struct mii_bus *bus, int addr);
void phy_sanitize_settings(struct phy_device *phydev); void phy_sanitize_settings(struct phy_device *phydev);
int phy_stop_interrupts(struct phy_device *phydev); int phy_stop_interrupts(struct phy_device *phydev);
int phy_enable_interrupts(struct phy_device *phydev); int phy_enable_interrupts(struct phy_device *phydev);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment