Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
L
linux
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
nexedi
linux
Commits
f3c1e4cf
Commit
f3c1e4cf
authored
Aug 22, 2004
by
Andy Fleming
Committed by
Jeff Garzik
Aug 22, 2004
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
[PATCH] update gianfar ethernet driver
parent
633980ae
Changes
7
Hide whitespace changes
Inline
Side-by-side
Showing
7 changed files
with
1049 additions
and
886 deletions
+1049
-886
drivers/net/Makefile
drivers/net/Makefile
+3
-1
drivers/net/gianfar.c
drivers/net/gianfar.c
+258
-315
drivers/net/gianfar.h
drivers/net/gianfar.h
+22
-24
drivers/net/gianfar_ethtool.c
drivers/net/gianfar_ethtool.c
+131
-90
drivers/net/gianfar_phy.c
drivers/net/gianfar_phy.c
+480
-323
drivers/net/gianfar_phy.h
drivers/net/gianfar_phy.h
+153
-132
include/linux/mii.h
include/linux/mii.h
+2
-1
No files found.
drivers/net/Makefile
View file @
f3c1e4cf
...
@@ -10,7 +10,9 @@ obj-$(CONFIG_E1000) += e1000/
...
@@ -10,7 +10,9 @@ obj-$(CONFIG_E1000) += e1000/
obj-$(CONFIG_IBM_EMAC)
+=
ibm_emac/
obj-$(CONFIG_IBM_EMAC)
+=
ibm_emac/
obj-$(CONFIG_IXGB)
+=
ixgb/
obj-$(CONFIG_IXGB)
+=
ixgb/
obj-$(CONFIG_BONDING)
+=
bonding/
obj-$(CONFIG_BONDING)
+=
bonding/
obj-$(CONFIG_GIANFAR)
+=
gianfar.o gianfar_ethtool.o gianfar_phy.o
obj-$(CONFIG_GIANFAR)
+=
gianfar_driver.o
gianfar_driver-objs
:=
gianfar.o gianfar_ethtool.o gianfar_phy.o
#
#
# link order important here
# link order important here
...
...
drivers/net/gianfar.c
View file @
f3c1e4cf
...
@@ -8,7 +8,7 @@
...
@@ -8,7 +8,7 @@
* Author: Andy Fleming
* Author: Andy Fleming
* Maintainer: Kumar Gala (kumar.gala@freescale.com)
* Maintainer: Kumar Gala (kumar.gala@freescale.com)
*
*
* Copyright
2004 Freescale Semiconductor, Inc
* Copyright
(c) 2002-2004 Freescale Semiconductor, Inc.
*
*
* This program is free software; you can redistribute it and/or modify it
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* under the terms of the GNU General Public License as published by the
...
@@ -96,15 +96,6 @@
...
@@ -96,15 +96,6 @@
#include "gianfar.h"
#include "gianfar.h"
#include "gianfar_phy.h"
#include "gianfar_phy.h"
#ifdef CONFIG_NET_FASTROUTE
#include <linux/if_arp.h>
#include <net/ip.h>
#endif
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,41)
#define irqreturn_t void
#define IRQ_HANDLED
#endif
#define TX_TIMEOUT (1*HZ)
#define TX_TIMEOUT (1*HZ)
#define SKB_ALLOC_TIMEOUT 1000000
#define SKB_ALLOC_TIMEOUT 1000000
...
@@ -117,9 +108,8 @@
...
@@ -117,9 +108,8 @@
#define RECEIVE(x) netif_rx(x)
#define RECEIVE(x) netif_rx(x)
#endif
#endif
#define DEVICE_NAME "%s: Gianfar Ethernet Controller Version 1.0, "
const
char
gfar_driver_name
[]
=
"Gianfar Ethernet"
;
char
gfar_driver_name
[]
=
"Gianfar Ethernet"
;
const
char
gfar_driver_version
[]
=
"1.1"
;
char
gfar_driver_version
[]
=
"1.0"
;
int
startup_gfar
(
struct
net_device
*
dev
);
int
startup_gfar
(
struct
net_device
*
dev
);
static
int
gfar_enet_open
(
struct
net_device
*
dev
);
static
int
gfar_enet_open
(
struct
net_device
*
dev
);
...
@@ -148,24 +138,11 @@ static void gfar_set_hash_for_addr(struct net_device *dev, u8 *addr);
...
@@ -148,24 +138,11 @@ static void gfar_set_hash_for_addr(struct net_device *dev, u8 *addr);
#ifdef CONFIG_GFAR_NAPI
#ifdef CONFIG_GFAR_NAPI
static
int
gfar_poll
(
struct
net_device
*
dev
,
int
*
budget
);
static
int
gfar_poll
(
struct
net_device
*
dev
,
int
*
budget
);
#endif
#endif
#ifdef CONFIG_NET_FASTROUTE
static
int
gfar_accept_fastpath
(
struct
net_device
*
dev
,
struct
dst_entry
*
dst
);
#endif
static
inline
int
try_fastroute
(
struct
sk_buff
*
skb
,
struct
net_device
*
dev
,
int
length
);
#ifdef CONFIG_GFAR_NAPI
static
int
gfar_clean_rx_ring
(
struct
net_device
*
dev
,
int
rx_work_limit
);
static
int
gfar_clean_rx_ring
(
struct
net_device
*
dev
,
int
rx_work_limit
);
#else
static
int
gfar_clean_rx_ring
(
struct
net_device
*
dev
);
#endif
static
int
gfar_process_frame
(
struct
net_device
*
dev
,
struct
sk_buff
*
skb
,
int
length
);
static
int
gfar_process_frame
(
struct
net_device
*
dev
,
struct
sk_buff
*
skb
,
int
length
);
static
void
gfar_phy_startup_timer
(
unsigned
long
data
);
extern
struct
ethtool_ops
gfar_ethtool_ops
;
extern
struct
ethtool_ops
gfar_ethtool_ops
;
extern
void
gfar_gstrings_normon
(
struct
net_device
*
dev
,
u32
stringset
,
u8
*
buf
);
extern
void
gfar_fill_stats_normon
(
struct
net_device
*
dev
,
struct
ethtool_stats
*
dummy
,
u64
*
buf
);
extern
int
gfar_stats_count_normon
(
struct
net_device
*
dev
);
MODULE_AUTHOR
(
"Freescale Semiconductor, Inc"
);
MODULE_AUTHOR
(
"Freescale Semiconductor, Inc"
);
MODULE_DESCRIPTION
(
"Gianfar Ethernet Driver"
);
MODULE_DESCRIPTION
(
"Gianfar Ethernet Driver"
);
...
@@ -183,7 +160,7 @@ static int gfar_probe(struct ocp_device *ocpdev)
...
@@ -183,7 +160,7 @@ static int gfar_probe(struct ocp_device *ocpdev)
struct
ocp_gfar_data
*
einfo
;
struct
ocp_gfar_data
*
einfo
;
int
idx
;
int
idx
;
int
err
=
0
;
int
err
=
0
;
struct
ethtool_ops
*
dev_ethtool_ops
;
int
dev_ethtool_ops
=
0
;
einfo
=
(
struct
ocp_gfar_data
*
)
ocpdev
->
def
->
additions
;
einfo
=
(
struct
ocp_gfar_data
*
)
ocpdev
->
def
->
additions
;
...
@@ -197,7 +174,8 @@ static int gfar_probe(struct ocp_device *ocpdev)
...
@@ -197,7 +174,8 @@ static int gfar_probe(struct ocp_device *ocpdev)
/* get a pointer to the register memory which can
/* get a pointer to the register memory which can
* configure the PHYs. If it's different from this set,
* configure the PHYs. If it's different from this set,
* get the device which has those regs */
* get the device which has those regs */
if
((
einfo
->
phyregidx
>=
0
)
&&
(
einfo
->
phyregidx
!=
ocpdev
->
def
->
index
))
{
if
((
einfo
->
phyregidx
>=
0
)
&&
(
einfo
->
phyregidx
!=
ocpdev
->
def
->
index
))
{
mdiodev
=
ocp_find_device
(
OCP_ANY_ID
,
mdiodev
=
ocp_find_device
(
OCP_ANY_ID
,
OCP_FUNC_GFAR
,
einfo
->
phyregidx
);
OCP_FUNC_GFAR
,
einfo
->
phyregidx
);
...
@@ -222,7 +200,7 @@ static int gfar_probe(struct ocp_device *ocpdev)
...
@@ -222,7 +200,7 @@ static int gfar_probe(struct ocp_device *ocpdev)
/* get a pointer to the register memory */
/* get a pointer to the register memory */
priv
->
regs
=
(
struct
gfar
*
)
priv
->
regs
=
(
struct
gfar
*
)
ioremap
(
ocpdev
->
def
->
paddr
,
sizeof
(
struct
gfar
));
ioremap
(
ocpdev
->
def
->
paddr
,
sizeof
(
struct
gfar
));
if
(
priv
->
regs
==
NULL
)
{
if
(
priv
->
regs
==
NULL
)
{
err
=
-
ENOMEM
;
err
=
-
ENOMEM
;
...
@@ -238,6 +216,8 @@ static int gfar_probe(struct ocp_device *ocpdev)
...
@@ -238,6 +216,8 @@ static int gfar_probe(struct ocp_device *ocpdev)
goto
phy_regs_fail
;
goto
phy_regs_fail
;
}
}
spin_lock_init
(
&
priv
->
lock
);
ocp_set_drvdata
(
ocpdev
,
dev
);
ocp_set_drvdata
(
ocpdev
,
dev
);
/* Stop the DMA engine now, in case it was running before */
/* Stop the DMA engine now, in case it was running before */
...
@@ -269,15 +249,13 @@ static int gfar_probe(struct ocp_device *ocpdev)
...
@@ -269,15 +249,13 @@ static int gfar_probe(struct ocp_device *ocpdev)
gfar_write
(
&
priv
->
regs
->
ecntrl
,
ECNTRL_INIT_SETTINGS
);
gfar_write
(
&
priv
->
regs
->
ecntrl
,
ECNTRL_INIT_SETTINGS
);
/* Copy the station address into the dev structure, */
/* Copy the station address into the dev structure, */
/* and into the address registers MAC_STNADDR1,2. */
/* Backwards, because little endian MACs are dumb. */
/* Don't set the regs if the firmware already did */
memcpy
(
dev
->
dev_addr
,
einfo
->
mac_addr
,
MAC_ADDR_LEN
);
memcpy
(
dev
->
dev_addr
,
einfo
->
mac_addr
,
MAC_ADDR_LEN
);
/* Set the dev->base_addr to the gfar reg region */
/* Set the dev->base_addr to the gfar reg region */
dev
->
base_addr
=
(
unsigned
long
)
(
priv
->
regs
);
dev
->
base_addr
=
(
unsigned
long
)
(
priv
->
regs
);
SET_MODULE_OWNER
(
dev
);
SET_MODULE_OWNER
(
dev
);
SET_NETDEV_DEV
(
dev
,
&
ocpdev
->
dev
);
/* Fill in the dev structure */
/* Fill in the dev structure */
dev
->
open
=
gfar_enet_open
;
dev
->
open
=
gfar_enet_open
;
...
@@ -293,37 +271,16 @@ static int gfar_probe(struct ocp_device *ocpdev)
...
@@ -293,37 +271,16 @@ static int gfar_probe(struct ocp_device *ocpdev)
dev
->
change_mtu
=
gfar_change_mtu
;
dev
->
change_mtu
=
gfar_change_mtu
;
dev
->
mtu
=
1500
;
dev
->
mtu
=
1500
;
dev
->
set_multicast_list
=
gfar_set_multi
;
dev
->
set_multicast_list
=
gfar_set_multi
;
dev
->
flags
|=
IFF_MULTICAST
;
dev_ethtool_ops
=
(
struct
ethtool_ops
*
)
kmalloc
(
sizeof
(
struct
ethtool_ops
),
GFP_KERNEL
);
if
(
dev_ethtool_ops
==
NULL
)
{
/* Index into the array of possible ethtool
err
=
-
ENOMEM
;
* ops to catch all 4 possibilities */
goto
ethtool_fail
;
if
((
priv
->
einfo
->
flags
&
GFAR_HAS_RMON
)
==
0
)
}
dev_ethtool_ops
+=
1
;
memcpy
(
dev_ethtool_ops
,
&
gfar_ethtool_ops
,
sizeof
(
gfar_ethtool_ops
));
if
((
priv
->
einfo
->
flags
&
GFAR_HAS_COALESCE
)
==
0
)
dev_ethtool_ops
+=
2
;
/* If there is no RMON support in this device, we don't
dev
->
ethtool_ops
=
gfar_op_array
[
dev_ethtool_ops
];
* want to expose non-existant statistics */
if
((
priv
->
einfo
->
flags
&
GFAR_HAS_RMON
)
==
0
)
{
dev_ethtool_ops
->
get_strings
=
gfar_gstrings_normon
;
dev_ethtool_ops
->
get_stats_count
=
gfar_stats_count_normon
;
dev_ethtool_ops
->
get_ethtool_stats
=
gfar_fill_stats_normon
;
}
if
((
priv
->
einfo
->
flags
&
GFAR_HAS_COALESCE
)
==
0
)
{
dev_ethtool_ops
->
set_coalesce
=
NULL
;
dev_ethtool_ops
->
get_coalesce
=
NULL
;
}
dev
->
ethtool_ops
=
dev_ethtool_ops
;
#ifdef CONFIG_NET_FASTROUTE
dev
->
accept_fastpath
=
gfar_accept_fastpath
;
#endif
priv
->
rx_buffer_size
=
DEFAULT_RX_BUFFER_SIZE
;
priv
->
rx_buffer_size
=
DEFAULT_RX_BUFFER_SIZE
;
#ifdef CONFIG_GFAR_BUFSTASH
#ifdef CONFIG_GFAR_BUFSTASH
...
@@ -332,27 +289,26 @@ static int gfar_probe(struct ocp_device *ocpdev)
...
@@ -332,27 +289,26 @@ static int gfar_probe(struct ocp_device *ocpdev)
priv
->
tx_ring_size
=
DEFAULT_TX_RING_SIZE
;
priv
->
tx_ring_size
=
DEFAULT_TX_RING_SIZE
;
priv
->
rx_ring_size
=
DEFAULT_RX_RING_SIZE
;
priv
->
rx_ring_size
=
DEFAULT_RX_RING_SIZE
;
/* Initially, coalescing is disabled */
priv
->
txcoalescing
=
DEFAULT_TX_COALESCE
;
priv
->
txcoalescing
=
0
;
priv
->
txcount
=
DEFAULT_TXCOUNT
;
priv
->
txcount
=
0
;
priv
->
txtime
=
DEFAULT_TXTIME
;
priv
->
txtime
=
0
;
priv
->
rxcoalescing
=
DEFAULT_RX_COALESCE
;
priv
->
rxcoalescing
=
0
;
priv
->
rxcount
=
DEFAULT_RXCOUNT
;
priv
->
rxcount
=
0
;
priv
->
rxtime
=
DEFAULT_RXTIME
;
priv
->
rxtime
=
0
;
err
=
register_netdev
(
dev
);
err
=
register_netdev
(
dev
);
if
(
err
)
{
if
(
err
)
{
printk
(
KERN_ERR
"%s: Cannot register net device, aborting.
\n
"
,
printk
(
KERN_ERR
"%s: Cannot register net device, aborting.
\n
"
,
dev
->
name
);
dev
->
name
);
goto
register_fail
;
goto
register_fail
;
}
}
/* Print out the device info */
/* Print out the device info */
printk
(
DEVICE_NAME
,
dev
->
name
);
printk
(
KERN_INFO
DEVICE_NAME
,
dev
->
name
);
for
(
idx
=
0
;
idx
<
6
;
idx
++
)
for
(
idx
=
0
;
idx
<
6
;
idx
++
)
printk
(
"%2.2x%c"
,
dev
->
dev_addr
[
idx
],
idx
==
5
?
' '
:
':'
);
printk
(
KERN_INFO
"%2.2x%c"
,
dev
->
dev_addr
[
idx
],
idx
==
5
?
' '
:
':'
);
printk
(
"
\n
"
);
printk
(
KERN_INFO
"
\n
"
);
/* Even more device info helps when determining which kernel */
/* Even more device info helps when determining which kernel */
/* provided which set of benchmarks. Since this is global for all */
/* provided which set of benchmarks. Since this is global for all */
...
@@ -367,10 +323,7 @@ static int gfar_probe(struct ocp_device *ocpdev)
...
@@ -367,10 +323,7 @@ static int gfar_probe(struct ocp_device *ocpdev)
return
0
;
return
0
;
register_fail:
register_fail:
kfree
(
dev_ethtool_ops
);
ethtool_fail:
iounmap
((
void
*
)
priv
->
phyregs
);
iounmap
((
void
*
)
priv
->
phyregs
);
phy_regs_fail:
phy_regs_fail:
iounmap
((
void
*
)
priv
->
regs
);
iounmap
((
void
*
)
priv
->
regs
);
...
@@ -386,7 +339,6 @@ static void gfar_remove(struct ocp_device *ocpdev)
...
@@ -386,7 +339,6 @@ static void gfar_remove(struct ocp_device *ocpdev)
ocp_set_drvdata
(
ocpdev
,
NULL
);
ocp_set_drvdata
(
ocpdev
,
NULL
);
kfree
(
dev
->
ethtool_ops
);
iounmap
((
void
*
)
priv
->
regs
);
iounmap
((
void
*
)
priv
->
regs
);
iounmap
((
void
*
)
priv
->
phyregs
);
iounmap
((
void
*
)
priv
->
phyregs
);
free_netdev
(
dev
);
free_netdev
(
dev
);
...
@@ -399,26 +351,90 @@ static int init_phy(struct net_device *dev)
...
@@ -399,26 +351,90 @@ static int init_phy(struct net_device *dev)
{
{
struct
gfar_private
*
priv
=
netdev_priv
(
dev
);
struct
gfar_private
*
priv
=
netdev_priv
(
dev
);
struct
phy_info
*
curphy
;
struct
phy_info
*
curphy
;
unsigned
int
timeout
=
PHY_INIT_TIMEOUT
;
struct
gfar
*
phyregs
=
priv
->
phyregs
;
struct
gfar_mii_info
*
mii_info
;
int
err
;
priv
->
link
=
1
;
priv
->
oldlink
=
0
;
priv
->
oldlink
=
0
;
priv
->
oldspeed
=
0
;
priv
->
oldspeed
=
0
;
priv
->
olddplx
=
-
1
;
priv
->
oldduplex
=
-
1
;
mii_info
=
kmalloc
(
sizeof
(
struct
gfar_mii_info
),
GFP_KERNEL
);
if
(
NULL
==
mii_info
)
{
printk
(
KERN_ERR
"%s: Could not allocate mii_info
\n
"
,
dev
->
name
);
return
-
ENOMEM
;
}
mii_info
->
speed
=
SPEED_1000
;
mii_info
->
duplex
=
DUPLEX_FULL
;
mii_info
->
pause
=
0
;
mii_info
->
link
=
1
;
mii_info
->
advertising
=
(
ADVERTISED_10baseT_Half
|
ADVERTISED_10baseT_Full
|
ADVERTISED_100baseT_Half
|
ADVERTISED_100baseT_Full
|
ADVERTISED_1000baseT_Full
);
mii_info
->
autoneg
=
1
;
mii_info
->
mii_id
=
priv
->
einfo
->
phyid
;
mii_info
->
dev
=
dev
;
mii_info
->
mdio_read
=
&
read_phy_reg
;
mii_info
->
mdio_write
=
&
write_phy_reg
;
priv
->
mii_info
=
mii_info
;
/* Reset the management interface */
gfar_write
(
&
phyregs
->
miimcfg
,
MIIMCFG_RESET
);
/* Setup the MII Mgmt clock speed */
gfar_write
(
&
phyregs
->
miimcfg
,
MIIMCFG_INIT_VALUE
);
/* Wait until the bus is free */
while
((
gfar_read
(
&
phyregs
->
miimind
)
&
MIIMIND_BUSY
)
&&
timeout
--
)
cpu_relax
();
if
(
timeout
<=
0
)
{
printk
(
KERN_ERR
"%s: The MII Bus is stuck!
\n
"
,
dev
->
name
);
err
=
-
1
;
goto
bus_fail
;
}
/* get info for this PHY */
/* get info for this PHY */
curphy
=
get_phy_info
(
dev
);
curphy
=
get_phy_info
(
priv
->
mii_info
);
if
(
curphy
==
NULL
)
{
if
(
curphy
==
NULL
)
{
printk
(
KERN_ERR
"%s: No PHY found
\n
"
,
dev
->
name
);
printk
(
KERN_ERR
"%s: No PHY found
\n
"
,
dev
->
name
);
return
-
1
;
err
=
-
1
;
goto
no_phy
;
}
}
priv
->
phyinfo
=
curphy
;
mii_info
->
phyinfo
=
curphy
;
/* Run the commands which configure the PHY */
/* Run the commands which initialize the PHY */
phy_run_commands
(
dev
,
curphy
->
config
);
if
(
curphy
->
init
)
{
err
=
curphy
->
init
(
priv
->
mii_info
);
if
(
err
)
goto
phy_init_fail
;
}
return
0
;
return
0
;
phy_init_fail:
no_phy:
bus_fail:
kfree
(
mii_info
);
return
err
;
}
}
static
void
init_registers
(
struct
net_device
*
dev
)
static
void
init_registers
(
struct
net_device
*
dev
)
...
@@ -494,7 +510,7 @@ void stop_gfar(struct net_device *dev)
...
@@ -494,7 +510,7 @@ void stop_gfar(struct net_device *dev)
spin_lock_irqsave
(
&
priv
->
lock
,
flags
);
spin_lock_irqsave
(
&
priv
->
lock
,
flags
);
/* Tell the kernel the link is down */
/* Tell the kernel the link is down */
priv
->
link
=
0
;
priv
->
mii_info
->
link
=
0
;
adjust_link
(
dev
);
adjust_link
(
dev
);
/* Mask all interrupts */
/* Mask all interrupts */
...
@@ -521,7 +537,12 @@ void stop_gfar(struct net_device *dev)
...
@@ -521,7 +537,12 @@ void stop_gfar(struct net_device *dev)
gfar_write
(
&
regs
->
maccfg1
,
tempval
);
gfar_write
(
&
regs
->
maccfg1
,
tempval
);
if
(
priv
->
einfo
->
flags
&
GFAR_HAS_PHY_INTR
)
{
if
(
priv
->
einfo
->
flags
&
GFAR_HAS_PHY_INTR
)
{
phy_run_commands
(
dev
,
priv
->
phyinfo
->
shutdown
);
/* Clear any pending interrupts */
mii_clear_phy_interrupt
(
priv
->
mii_info
);
/* Disable PHY Interrupts */
mii_configure_phy_interrupt
(
priv
->
mii_info
,
MII_INTERRUPT_DISABLED
);
}
}
spin_unlock_irqrestore
(
&
priv
->
lock
,
flags
);
spin_unlock_irqrestore
(
&
priv
->
lock
,
flags
);
...
@@ -543,15 +564,11 @@ void stop_gfar(struct net_device *dev)
...
@@ -543,15 +564,11 @@ void stop_gfar(struct net_device *dev)
free_skb_resources
(
priv
);
free_skb_resources
(
priv
);
dma_unmap_single
(
NULL
,
gfar_read
(
&
regs
->
tbase
),
dma_free_coherent
(
NULL
,
sizeof
(
struct
txbd
)
*
priv
->
tx_ring_size
,
sizeof
(
struct
txbd8
)
*
priv
->
tx_ring_size
DMA_BIDIRECTIONAL
);
+
sizeof
(
struct
rxbd8
)
*
priv
->
rx_ring_size
,
dma_unmap_single
(
NULL
,
gfar_read
(
&
regs
->
rbase
),
priv
->
tx_bd_base
,
sizeof
(
struct
rxbd
)
*
priv
->
rx_ring_size
,
gfar_read
(
&
regs
->
tbase
));
DMA_BIDIRECTIONAL
);
/* Free the buffer descriptors */
kfree
(
priv
->
tx_bd_base
);
}
}
/* If there are any tx skbs or rx skbs still around, free them.
/* If there are any tx skbs or rx skbs still around, free them.
...
@@ -610,7 +627,8 @@ int startup_gfar(struct net_device *dev)
...
@@ -610,7 +627,8 @@ int startup_gfar(struct net_device *dev)
{
{
struct
txbd8
*
txbdp
;
struct
txbd8
*
txbdp
;
struct
rxbd8
*
rxbdp
;
struct
rxbd8
*
rxbdp
;
unsigned
long
addr
;
dma_addr_t
addr
;
unsigned
long
vaddr
;
int
i
;
int
i
;
struct
gfar_private
*
priv
=
netdev_priv
(
dev
);
struct
gfar_private
*
priv
=
netdev_priv
(
dev
);
struct
gfar
*
regs
=
priv
->
regs
;
struct
gfar
*
regs
=
priv
->
regs
;
...
@@ -620,32 +638,27 @@ int startup_gfar(struct net_device *dev)
...
@@ -620,32 +638,27 @@ int startup_gfar(struct net_device *dev)
gfar_write
(
&
regs
->
imask
,
IMASK_INIT_CLEAR
);
gfar_write
(
&
regs
->
imask
,
IMASK_INIT_CLEAR
);
/* Allocate memory for the buffer descriptors */
/* Allocate memory for the buffer descriptors */
addr
=
vaddr
=
(
unsigned
long
)
dma_alloc_coherent
(
NULL
,
(
unsigned
int
)
kmalloc
(
sizeof
(
struct
txbd8
)
*
priv
->
tx_ring_size
+
sizeof
(
struct
txbd8
)
*
priv
->
tx_ring_size
+
sizeof
(
struct
rxbd8
)
*
priv
->
rx_ring_size
,
sizeof
(
struct
rxbd8
)
*
priv
->
rx_ring_size
,
GFP_KERNEL
);
&
addr
,
GFP_KERNEL
);
if
(
addr
==
0
)
{
if
(
v
addr
==
0
)
{
printk
(
KERN_ERR
"%s: Could not allocate buffer descriptors!
\n
"
,
printk
(
KERN_ERR
"%s: Could not allocate buffer descriptors!
\n
"
,
dev
->
name
);
dev
->
name
);
return
-
ENOMEM
;
return
-
ENOMEM
;
}
}
priv
->
tx_bd_base
=
(
struct
txbd8
*
)
addr
;
priv
->
tx_bd_base
=
(
struct
txbd8
*
)
v
addr
;
/* enet DMA only understands physical addresses */
/* enet DMA only understands physical addresses */
gfar_write
(
&
regs
->
tbase
,
gfar_write
(
&
regs
->
tbase
,
addr
);
dma_map_single
(
NULL
,
(
void
*
)
addr
,
sizeof
(
struct
txbd8
)
*
priv
->
tx_ring_size
,
DMA_BIDIRECTIONAL
));
/* Start the rx descriptor ring where the tx ring leaves off */
/* Start the rx descriptor ring where the tx ring leaves off */
addr
=
addr
+
sizeof
(
struct
txbd8
)
*
priv
->
tx_ring_size
;
addr
=
addr
+
sizeof
(
struct
txbd8
)
*
priv
->
tx_ring_size
;
priv
->
rx_bd_base
=
(
struct
rxbd8
*
)
addr
;
vaddr
=
vaddr
+
sizeof
(
struct
txbd8
)
*
priv
->
tx_ring_size
;
gfar_write
(
&
regs
->
rbase
,
priv
->
rx_bd_base
=
(
struct
rxbd8
*
)
vaddr
;
dma_map_single
(
NULL
,
(
void
*
)
addr
,
gfar_write
(
&
regs
->
rbase
,
addr
);
sizeof
(
struct
rxbd8
)
*
priv
->
rx_ring_size
,
DMA_BIDIRECTIONAL
));
/* Setup the skbuff rings */
/* Setup the skbuff rings */
priv
->
tx_skbuff
=
priv
->
tx_skbuff
=
...
@@ -755,39 +768,13 @@ int startup_gfar(struct net_device *dev)
...
@@ -755,39 +768,13 @@ int startup_gfar(struct net_device *dev)
}
}
}
}
/* Grab the PHY interrupt */
/* Set up the PHY change work queue */
if
(
priv
->
einfo
->
flags
&
GFAR_HAS_PHY_INTR
)
{
INIT_WORK
(
&
priv
->
tq
,
gfar_phy_change
,
dev
);
if
(
request_irq
(
priv
->
einfo
->
interruptPHY
,
phy_interrupt
,
SA_SHIRQ
,
"phy_interrupt"
,
dev
)
<
0
)
{
printk
(
KERN_ERR
"%s: Can't get IRQ %d (PHY)
\n
"
,
dev
->
name
,
priv
->
einfo
->
interruptPHY
);
err
=
-
1
;
if
(
priv
->
einfo
->
flags
&
GFAR_HAS_MULTI_INTR
)
goto
phy_irq_fail
;
else
goto
tx_irq_fail
;
}
}
else
{
init_timer
(
&
priv
->
phy_info_timer
);
priv
->
phy_info_timer
.
function
=
&
gfar_phy_timer
;
priv
->
phy_info_timer
.
data
=
(
unsigned
long
)
dev
;
mod_timer
(
&
priv
->
phy_info_timer
,
jiffies
+
2
*
HZ
);
}
/* Set up the bottom half queue */
INIT_WORK
(
&
priv
->
tq
,
(
void
(
*
)(
void
*
))
gfar_phy_change
,
dev
);
/* Configure the PHY interrupt */
phy_run_commands
(
dev
,
priv
->
phyinfo
->
startup
);
/* Tell the kernel the link is up, and determine the
init_timer
(
&
priv
->
phy_info_timer
);
* negotiated features (speed, duplex) */
priv
->
phy_info_timer
.
function
=
&
gfar_phy_startup_timer
;
adjust_link
(
dev
);
priv
->
phy_info_timer
.
data
=
(
unsigned
long
)
priv
->
mii_info
;
mod_timer
(
&
priv
->
phy_info_timer
,
jiffies
+
HZ
);
if
(
priv
->
link
==
0
)
printk
(
KERN_INFO
"%s: No link detected
\n
"
,
dev
->
name
);
/* Configure the coalescing support */
/* Configure the coalescing support */
if
(
priv
->
txcoalescing
)
if
(
priv
->
txcoalescing
)
...
@@ -827,8 +814,6 @@ int startup_gfar(struct net_device *dev)
...
@@ -827,8 +814,6 @@ int startup_gfar(struct net_device *dev)
return
0
;
return
0
;
phy_irq_fail:
free_irq
(
priv
->
einfo
->
interruptReceive
,
dev
);
rx_irq_fail:
rx_irq_fail:
free_irq
(
priv
->
einfo
->
interruptTransmit
,
dev
);
free_irq
(
priv
->
einfo
->
interruptTransmit
,
dev
);
tx_irq_fail:
tx_irq_fail:
...
@@ -837,7 +822,17 @@ int startup_gfar(struct net_device *dev)
...
@@ -837,7 +822,17 @@ int startup_gfar(struct net_device *dev)
rx_skb_fail:
rx_skb_fail:
free_skb_resources
(
priv
);
free_skb_resources
(
priv
);
tx_skb_fail:
tx_skb_fail:
kfree
(
priv
->
tx_bd_base
);
dma_free_coherent
(
NULL
,
sizeof
(
struct
txbd8
)
*
priv
->
tx_ring_size
+
sizeof
(
struct
rxbd8
)
*
priv
->
rx_ring_size
,
priv
->
tx_bd_base
,
gfar_read
(
&
regs
->
tbase
));
if
(
priv
->
mii_info
->
phyinfo
->
close
)
priv
->
mii_info
->
phyinfo
->
close
(
priv
->
mii_info
);
kfree
(
priv
->
mii_info
);
return
err
;
return
err
;
}
}
...
@@ -854,7 +849,7 @@ static int gfar_enet_open(struct net_device *dev)
...
@@ -854,7 +849,7 @@ static int gfar_enet_open(struct net_device *dev)
err
=
init_phy
(
dev
);
err
=
init_phy
(
dev
);
if
(
err
)
if
(
err
)
return
err
;
return
err
;
err
=
startup_gfar
(
dev
);
err
=
startup_gfar
(
dev
);
...
@@ -934,8 +929,15 @@ static int gfar_start_xmit(struct sk_buff *skb, struct net_device *dev)
...
@@ -934,8 +929,15 @@ static int gfar_start_xmit(struct sk_buff *skb, struct net_device *dev)
/* Stops the kernel queue, and halts the controller */
/* Stops the kernel queue, and halts the controller */
static
int
gfar_close
(
struct
net_device
*
dev
)
static
int
gfar_close
(
struct
net_device
*
dev
)
{
{
struct
gfar_private
*
priv
=
netdev_priv
(
dev
);
stop_gfar
(
dev
);
stop_gfar
(
dev
);
/* Shutdown the PHY */
if
(
priv
->
mii_info
->
phyinfo
->
close
)
priv
->
mii_info
->
phyinfo
->
close
(
priv
->
mii_info
);
kfree
(
priv
->
mii_info
);
netif_stop_queue
(
dev
);
netif_stop_queue
(
dev
);
return
0
;
return
0
;
...
@@ -971,121 +973,6 @@ int gfar_set_mac_address(struct net_device *dev)
...
@@ -971,121 +973,6 @@ int gfar_set_mac_address(struct net_device *dev)
return
0
;
return
0
;
}
}
/**********************************************************************
* gfar_accept_fastpath
*
* Used to authenticate to the kernel that a fast path entry can be
* added to device's routing table cache
*
* Input : pointer to ethernet interface network device structure and
* a pointer to the designated entry to be added to the cache.
* Output : zero upon success, negative upon failure
**********************************************************************/
#ifdef CONFIG_NET_FASTROUTE
static
int
gfar_accept_fastpath
(
struct
net_device
*
dev
,
struct
dst_entry
*
dst
)
{
struct
net_device
*
odev
=
dst
->
dev
;
if
((
dst
->
ops
->
protocol
!=
__constant_htons
(
ETH_P_IP
))
||
(
odev
->
type
!=
ARPHRD_ETHER
)
||
(
odev
->
accept_fastpath
==
NULL
))
{
return
-
1
;
}
return
0
;
}
#endif
/* try_fastroute() -- Checks the fastroute cache to see if a given packet
* can be routed immediately to another device. If it can, we send it.
* If we used a fastroute, we return 1. Otherwise, we return 0.
* Returns 0 if CONFIG_NET_FASTROUTE is not on
*/
static
inline
int
try_fastroute
(
struct
sk_buff
*
skb
,
struct
net_device
*
dev
,
int
length
)
{
#ifdef CONFIG_NET_FASTROUTE
struct
ethhdr
*
eth
;
struct
iphdr
*
iph
;
unsigned
int
hash
;
struct
rtable
*
rt
;
struct
net_device
*
odev
;
struct
gfar_private
*
priv
=
netdev_priv
(
dev
);
unsigned
int
CPU_ID
=
smp_processor_id
();
eth
=
(
struct
ethhdr
*
)
(
skb
->
data
);
/* Only route ethernet IP packets */
if
(
eth
->
h_proto
==
__constant_htons
(
ETH_P_IP
))
{
iph
=
(
struct
iphdr
*
)
(
skb
->
data
+
ETH_HLEN
);
/* Generate the hash value */
hash
=
((
*
(
u8
*
)
&
iph
->
daddr
)
^
(
*
(
u8
*
)
&
iph
->
saddr
))
&
NETDEV_FASTROUTE_HMASK
;
rt
=
(
struct
rtable
*
)
(
dev
->
fastpath
[
hash
]);
if
(
rt
!=
NULL
&&
((
*
(
u32
*
)
&
iph
->
daddr
)
==
(
*
(
u32
*
)
&
rt
->
key
.
dst
))
&&
((
*
(
u32
*
)
&
iph
->
saddr
)
==
(
*
(
u32
*
)
&
rt
->
key
.
src
))
&&
!
(
rt
->
u
.
dst
.
obsolete
))
{
odev
=
rt
->
u
.
dst
.
dev
;
netdev_rx_stat
[
CPU_ID
].
fastroute_hit
++
;
/* Make sure the packet is:
* 1) IPv4
* 2) without any options (header length of 5)
* 3) Not a multicast packet
* 4) going to a valid destination
* 5) Not out of time-to-live
*/
if
(
iph
->
version
==
4
&&
iph
->
ihl
==
5
&&
(
!
(
eth
->
h_dest
[
0
]
&
0x01
))
&&
neigh_is_valid
(
rt
->
u
.
dst
.
neighbour
)
&&
iph
->
ttl
>
1
)
{
/* Fast Route Path: Taken if the outgoing device is ready to transmit the packet now */
if
((
!
netif_queue_stopped
(
odev
))
&&
(
!
spin_is_locked
(
odev
->
xmit_lock
))
&&
(
skb
->
len
<=
(
odev
->
mtu
+
ETH_HLEN
+
2
+
4
)))
{
skb
->
pkt_type
=
PACKET_FASTROUTE
;
skb
->
protocol
=
__constant_htons
(
ETH_P_IP
);
ip_decrease_ttl
(
iph
);
memcpy
(
eth
->
h_source
,
odev
->
dev_addr
,
MAC_ADDR_LEN
);
memcpy
(
eth
->
h_dest
,
rt
->
u
.
dst
.
neighbour
->
ha
,
MAC_ADDR_LEN
);
skb
->
dev
=
odev
;
/* Prep the skb for the packet */
skb_put
(
skb
,
length
);
if
(
odev
->
hard_start_xmit
(
skb
,
odev
)
!=
0
)
{
panic
(
"%s: FastRoute path corrupted"
,
dev
->
name
);
}
netdev_rx_stat
[
CPU_ID
].
fastroute_success
++
;
}
/* Semi Fast Route Path: Mark the packet as needing fast routing, but let the
* stack handle getting it to the device */
else
{
skb
->
pkt_type
=
PACKET_FASTROUTE
;
skb
->
nh
.
raw
=
skb
->
data
+
ETH_HLEN
;
skb
->
protocol
=
__constant_htons
(
ETH_P_IP
);
netdev_rx_stat
[
CPU_ID
].
fastroute_defer
++
;
/* Prep the skb for the packet */
skb_put
(
skb
,
length
);
if
(
RECEIVE
(
skb
)
==
NET_RX_DROP
)
{
priv
->
extra_stats
.
kernel_dropped
++
;
}
}
return
1
;
}
}
}
#endif
/* CONFIG_NET_FASTROUTE */
return
0
;
}
static
int
gfar_change_mtu
(
struct
net_device
*
dev
,
int
new_mtu
)
static
int
gfar_change_mtu
(
struct
net_device
*
dev
,
int
new_mtu
)
{
{
...
@@ -1148,8 +1035,7 @@ static void gfar_timeout(struct net_device *dev)
...
@@ -1148,8 +1035,7 @@ static void gfar_timeout(struct net_device *dev)
startup_gfar
(
dev
);
startup_gfar
(
dev
);
}
}
if
(
!
netif_queue_stopped
(
dev
))
netif_schedule
(
dev
);
netif_schedule
(
dev
);
}
}
/* Interrupt Handler for Transmit complete */
/* Interrupt Handler for Transmit complete */
...
@@ -1315,7 +1201,7 @@ irqreturn_t gfar_receive(int irq, void *dev_id, struct pt_regs *regs)
...
@@ -1315,7 +1201,7 @@ irqreturn_t gfar_receive(int irq, void *dev_id, struct pt_regs *regs)
#else
#else
spin_lock
(
&
priv
->
lock
);
spin_lock
(
&
priv
->
lock
);
gfar_clean_rx_ring
(
dev
);
gfar_clean_rx_ring
(
dev
,
priv
->
rx_ring_size
);
/* If we are coalescing interrupts, update the timer */
/* If we are coalescing interrupts, update the timer */
/* Otherwise, clear it */
/* Otherwise, clear it */
...
@@ -1336,7 +1222,7 @@ irqreturn_t gfar_receive(int irq, void *dev_id, struct pt_regs *regs)
...
@@ -1336,7 +1222,7 @@ irqreturn_t gfar_receive(int irq, void *dev_id, struct pt_regs *regs)
/* gfar_process_frame() -- handle one incoming packet if skb
/* gfar_process_frame() -- handle one incoming packet if skb
* isn't NULL.
Try the fastroute before using the stack
*/
* isn't NULL. */
static
int
gfar_process_frame
(
struct
net_device
*
dev
,
struct
sk_buff
*
skb
,
static
int
gfar_process_frame
(
struct
net_device
*
dev
,
struct
sk_buff
*
skb
,
int
length
)
int
length
)
{
{
...
@@ -1350,17 +1236,15 @@ static int gfar_process_frame(struct net_device *dev, struct sk_buff *skb,
...
@@ -1350,17 +1236,15 @@ static int gfar_process_frame(struct net_device *dev, struct sk_buff *skb,
priv
->
stats
.
rx_dropped
++
;
priv
->
stats
.
rx_dropped
++
;
priv
->
extra_stats
.
rx_skbmissing
++
;
priv
->
extra_stats
.
rx_skbmissing
++
;
}
else
{
}
else
{
if
(
try_fastroute
(
skb
,
dev
,
length
)
==
0
)
{
/* Prep the skb for the packet */
/* Prep the skb for the packet */
skb_put
(
skb
,
length
);
skb_put
(
skb
,
length
);
/* Tell the skb what kind of packet this is */
/* Tell the skb what kind of packet this is */
skb
->
protocol
=
eth_type_trans
(
skb
,
dev
);
skb
->
protocol
=
eth_type_trans
(
skb
,
dev
);
/* Send the packet up the stack */
/* Send the packet up the stack */
if
(
RECEIVE
(
skb
)
==
NET_RX_DROP
)
{
if
(
RECEIVE
(
skb
)
==
NET_RX_DROP
)
{
priv
->
extra_stats
.
kernel_dropped
++
;
priv
->
extra_stats
.
kernel_dropped
++
;
}
}
}
}
}
...
@@ -1368,14 +1252,10 @@ static int gfar_process_frame(struct net_device *dev, struct sk_buff *skb,
...
@@ -1368,14 +1252,10 @@ static int gfar_process_frame(struct net_device *dev, struct sk_buff *skb,
}
}
/* gfar_clean_rx_ring() -- Processes each frame in the rx ring
/* gfar_clean_rx_ring() -- Processes each frame in the rx ring
* until
all are gone (or, in the case of NAPI, the budget/quota
* until
the budget/quota has been reached. Returns the number
*
has been reached). Returns the number
of frames handled
* of frames handled
*/
*/
#ifdef CONFIG_GFAR_NAPI
static
int
gfar_clean_rx_ring
(
struct
net_device
*
dev
,
int
rx_work_limit
)
static
int
gfar_clean_rx_ring
(
struct
net_device
*
dev
,
int
rx_work_limit
)
#else
static
int
gfar_clean_rx_ring
(
struct
net_device
*
dev
)
#endif
{
{
struct
rxbd8
*
bdp
;
struct
rxbd8
*
bdp
;
struct
sk_buff
*
skb
;
struct
sk_buff
*
skb
;
...
@@ -1386,12 +1266,7 @@ static int gfar_clean_rx_ring(struct net_device *dev)
...
@@ -1386,12 +1266,7 @@ static int gfar_clean_rx_ring(struct net_device *dev)
/* Get the first full descriptor */
/* Get the first full descriptor */
bdp
=
priv
->
cur_rx
;
bdp
=
priv
->
cur_rx
;
#ifdef CONFIG_GFAR_NAPI
while
(
!
((
bdp
->
status
&
RXBD_EMPTY
)
||
(
--
rx_work_limit
<
0
)))
{
#define GFAR_RXDONE() ((bdp->status & RXBD_EMPTY) || (--rx_work_limit < 0))
#else
#define GFAR_RXDONE() (bdp->status & RXBD_EMPTY)
#endif
while
(
!
GFAR_RXDONE
())
{
skb
=
priv
->
rx_skbuff
[
priv
->
skb_currx
];
skb
=
priv
->
rx_skbuff
[
priv
->
skb_currx
];
if
(
!
(
bdp
->
status
&
if
(
!
(
bdp
->
status
&
...
@@ -1407,7 +1282,6 @@ static int gfar_clean_rx_ring(struct net_device *dev)
...
@@ -1407,7 +1282,6 @@ static int gfar_clean_rx_ring(struct net_device *dev)
gfar_process_frame
(
dev
,
skb
,
pkt_len
);
gfar_process_frame
(
dev
,
skb
,
pkt_len
);
priv
->
stats
.
rx_bytes
+=
pkt_len
;
priv
->
stats
.
rx_bytes
+=
pkt_len
;
}
else
{
}
else
{
count_errors
(
bdp
->
status
,
priv
);
count_errors
(
bdp
->
status
,
priv
);
...
@@ -1462,7 +1336,6 @@ static int gfar_poll(struct net_device *dev, int *budget)
...
@@ -1462,7 +1336,6 @@ static int gfar_poll(struct net_device *dev, int *budget)
if
(
rx_work_limit
>
dev
->
quota
)
if
(
rx_work_limit
>
dev
->
quota
)
rx_work_limit
=
dev
->
quota
;
rx_work_limit
=
dev
->
quota
;
spin_lock
(
&
priv
->
lock
);
howmany
=
gfar_clean_rx_ring
(
dev
,
rx_work_limit
);
howmany
=
gfar_clean_rx_ring
(
dev
,
rx_work_limit
);
dev
->
quota
-=
howmany
;
dev
->
quota
-=
howmany
;
...
@@ -1489,8 +1362,6 @@ static int gfar_poll(struct net_device *dev, int *budget)
...
@@ -1489,8 +1362,6 @@ static int gfar_poll(struct net_device *dev, int *budget)
priv
->
rxclean
=
1
;
priv
->
rxclean
=
1
;
}
}
spin_unlock
(
priv
->
lock
);
return
(
rx_work_limit
<
0
)
?
1
:
0
;
return
(
rx_work_limit
<
0
)
?
1
:
0
;
}
}
#endif
#endif
...
@@ -1586,10 +1457,14 @@ static irqreturn_t phy_interrupt(int irq, void *dev_id, struct pt_regs *regs)
...
@@ -1586,10 +1457,14 @@ static irqreturn_t phy_interrupt(int irq, void *dev_id, struct pt_regs *regs)
struct
net_device
*
dev
=
(
struct
net_device
*
)
dev_id
;
struct
net_device
*
dev
=
(
struct
net_device
*
)
dev_id
;
struct
gfar_private
*
priv
=
netdev_priv
(
dev
);
struct
gfar_private
*
priv
=
netdev_priv
(
dev
);
/*
Run the commands which acknowledge
the interrupt */
/*
Clear
the interrupt */
phy_run_commands
(
dev
,
priv
->
phyinfo
->
ack_int
);
mii_clear_phy_interrupt
(
priv
->
mii_info
);
/* Schedule the bottom half */
/* Disable PHY interrupts */
mii_configure_phy_interrupt
(
priv
->
mii_info
,
MII_INTERRUPT_DISABLED
);
/* Schedule the phy change */
schedule_work
(
&
priv
->
tq
);
schedule_work
(
&
priv
->
tq
);
return
IRQ_HANDLED
;
return
IRQ_HANDLED
;
...
@@ -1600,18 +1475,24 @@ static void gfar_phy_change(void *data)
...
@@ -1600,18 +1475,24 @@ static void gfar_phy_change(void *data)
{
{
struct
net_device
*
dev
=
(
struct
net_device
*
)
data
;
struct
net_device
*
dev
=
(
struct
net_device
*
)
data
;
struct
gfar_private
*
priv
=
netdev_priv
(
dev
);
struct
gfar_private
*
priv
=
netdev_priv
(
dev
);
int
timeout
=
HZ
/
1000
+
1
;
int
result
=
0
;
/* Delay to give the PHY a chance to change the
/* Delay to give the PHY a chance to change the
* register state */
* register state */
set_current_state
(
TASK_UNINTERRUPTIBLE
);
msleep
(
1
);
schedule_timeout
(
timeout
);
/*
Run the commands which check the link state
*/
/*
Update the link, speed, duplex
*/
phy_run_commands
(
dev
,
priv
->
phyinfo
->
handle_int
);
result
=
priv
->
mii_info
->
phyinfo
->
read_status
(
priv
->
mii_info
);
/* React to the change in state */
/* Adjust the known status as long as the link
adjust_link
(
dev
);
* isn't still coming up */
if
((
0
==
result
)
||
(
priv
->
mii_info
->
link
==
0
))
adjust_link
(
dev
);
/* Reenable interrupts, if needed */
if
(
priv
->
einfo
->
flags
&
GFAR_HAS_PHY_INTR
)
mii_configure_phy_interrupt
(
priv
->
mii_info
,
MII_INTERRUPT_ENABLED
);
}
}
/* Called every so often on systems that don't interrupt
/* Called every so often on systems that don't interrupt
...
@@ -1623,7 +1504,72 @@ static void gfar_phy_timer(unsigned long data)
...
@@ -1623,7 +1504,72 @@ static void gfar_phy_timer(unsigned long data)
schedule_work
(
&
priv
->
tq
);
schedule_work
(
&
priv
->
tq
);
mod_timer
(
&
priv
->
phy_info_timer
,
jiffies
+
2
*
HZ
);
mod_timer
(
&
priv
->
phy_info_timer
,
jiffies
+
GFAR_PHY_CHANGE_TIME
*
HZ
);
}
/* Keep trying aneg for some time
* If, after GFAR_AN_TIMEOUT seconds, it has not
* finished, we switch to forced.
* Either way, once the process has completed, we either
* request the interrupt, or switch the timer over to
* using gfar_phy_timer to check status */
static
void
gfar_phy_startup_timer
(
unsigned
long
data
)
{
int
result
;
static
int
secondary
=
GFAR_AN_TIMEOUT
;
struct
gfar_mii_info
*
mii_info
=
(
struct
gfar_mii_info
*
)
data
;
struct
gfar_private
*
priv
=
netdev_priv
(
mii_info
->
dev
);
/* Configure the Auto-negotiation */
result
=
mii_info
->
phyinfo
->
config_aneg
(
mii_info
);
/* If autonegotiation failed to start, and
* we haven't timed out, reset the timer, and return */
if
(
result
&&
secondary
--
)
{
mod_timer
(
&
priv
->
phy_info_timer
,
jiffies
+
HZ
);
return
;
}
else
if
(
result
)
{
/* Couldn't start autonegotiation.
* Try switching to forced */
mii_info
->
autoneg
=
0
;
result
=
mii_info
->
phyinfo
->
config_aneg
(
mii_info
);
/* Forcing failed! Give up */
if
(
result
)
{
printk
(
KERN_ERR
"%s: Forcing failed!
\n
"
,
mii_info
->
dev
->
name
);
return
;
}
}
/* Kill the timer so it can be restarted */
del_timer_sync
(
&
priv
->
phy_info_timer
);
/* Grab the PHY interrupt, if necessary/possible */
if
(
priv
->
einfo
->
flags
&
GFAR_HAS_PHY_INTR
)
{
if
(
request_irq
(
priv
->
einfo
->
interruptPHY
,
phy_interrupt
,
SA_SHIRQ
,
"phy_interrupt"
,
mii_info
->
dev
)
<
0
)
{
printk
(
KERN_ERR
"%s: Can't get IRQ %d (PHY)
\n
"
,
mii_info
->
dev
->
name
,
priv
->
einfo
->
interruptPHY
);
}
else
{
mii_configure_phy_interrupt
(
priv
->
mii_info
,
MII_INTERRUPT_ENABLED
);
return
;
}
}
/* Start the timer again, this time in order to
* handle a change in status */
init_timer
(
&
priv
->
phy_info_timer
);
priv
->
phy_info_timer
.
function
=
&
gfar_phy_timer
;
priv
->
phy_info_timer
.
data
=
(
unsigned
long
)
mii_info
->
dev
;
mod_timer
(
&
priv
->
phy_info_timer
,
jiffies
+
GFAR_PHY_CHANGE_TIME
*
HZ
);
}
}
/* Called every time the controller might need to be made
/* Called every time the controller might need to be made
...
@@ -1637,12 +1583,13 @@ static void adjust_link(struct net_device *dev)
...
@@ -1637,12 +1583,13 @@ static void adjust_link(struct net_device *dev)
struct
gfar_private
*
priv
=
netdev_priv
(
dev
);
struct
gfar_private
*
priv
=
netdev_priv
(
dev
);
struct
gfar
*
regs
=
priv
->
regs
;
struct
gfar
*
regs
=
priv
->
regs
;
u32
tempval
;
u32
tempval
;
struct
gfar_mii_info
*
mii_info
=
priv
->
mii_info
;
if
(
priv
->
link
)
{
if
(
mii_info
->
link
)
{
/* Now we make sure that we can be in full duplex mode.
/* Now we make sure that we can be in full duplex mode.
* If not, we operate in half-duplex mode. */
* If not, we operate in half-duplex mode. */
if
(
priv
->
duplexity
!=
priv
->
olddpl
x
)
{
if
(
mii_info
->
duplex
!=
priv
->
oldduple
x
)
{
if
(
!
(
priv
->
duplexity
))
{
if
(
!
(
mii_info
->
duplex
))
{
tempval
=
gfar_read
(
&
regs
->
maccfg2
);
tempval
=
gfar_read
(
&
regs
->
maccfg2
);
tempval
&=
~
(
MACCFG2_FULL_DUPLEX
);
tempval
&=
~
(
MACCFG2_FULL_DUPLEX
);
gfar_write
(
&
regs
->
maccfg2
,
tempval
);
gfar_write
(
&
regs
->
maccfg2
,
tempval
);
...
@@ -1658,11 +1605,11 @@ static void adjust_link(struct net_device *dev)
...
@@ -1658,11 +1605,11 @@ static void adjust_link(struct net_device *dev)
dev
->
name
);
dev
->
name
);
}
}
priv
->
oldd
plx
=
priv
->
duplexity
;
priv
->
oldd
uplex
=
mii_info
->
duplex
;
}
}
if
(
priv
->
speed
!=
priv
->
oldspeed
)
{
if
(
mii_info
->
speed
!=
priv
->
oldspeed
)
{
switch
(
priv
->
speed
)
{
switch
(
mii_info
->
speed
)
{
case
1000
:
case
1000
:
tempval
=
gfar_read
(
&
regs
->
maccfg2
);
tempval
=
gfar_read
(
&
regs
->
maccfg2
);
tempval
=
tempval
=
...
@@ -1679,14 +1626,14 @@ static void adjust_link(struct net_device *dev)
...
@@ -1679,14 +1626,14 @@ static void adjust_link(struct net_device *dev)
default:
default:
printk
(
KERN_WARNING
printk
(
KERN_WARNING
"%s: Ack! Speed (%d) is not 10/100/1000!
\n
"
,
"%s: Ack! Speed (%d) is not 10/100/1000!
\n
"
,
dev
->
name
,
priv
->
speed
);
dev
->
name
,
mii_info
->
speed
);
break
;
break
;
}
}
printk
(
KERN_INFO
"%s: Speed %dBT
\n
"
,
dev
->
name
,
printk
(
KERN_INFO
"%s: Speed %dBT
\n
"
,
dev
->
name
,
priv
->
speed
);
mii_info
->
speed
);
priv
->
oldspeed
=
priv
->
speed
;
priv
->
oldspeed
=
mii_info
->
speed
;
}
}
if
(
!
priv
->
oldlink
)
{
if
(
!
priv
->
oldlink
)
{
...
@@ -1700,7 +1647,7 @@ static void adjust_link(struct net_device *dev)
...
@@ -1700,7 +1647,7 @@ static void adjust_link(struct net_device *dev)
printk
(
KERN_INFO
"%s: Link is down
\n
"
,
dev
->
name
);
printk
(
KERN_INFO
"%s: Link is down
\n
"
,
dev
->
name
);
priv
->
oldlink
=
0
;
priv
->
oldlink
=
0
;
priv
->
oldspeed
=
0
;
priv
->
oldspeed
=
0
;
priv
->
oldd
pl
x
=
-
1
;
priv
->
oldd
uple
x
=
-
1
;
netif_carrier_off
(
dev
);
netif_carrier_off
(
dev
);
}
}
}
}
...
@@ -1900,11 +1847,7 @@ static int __init gfar_init(void)
...
@@ -1900,11 +1847,7 @@ static int __init gfar_init(void)
int
rc
;
int
rc
;
rc
=
ocp_register_driver
(
&
gfar_driver
);
rc
=
ocp_register_driver
(
&
gfar_driver
);
#if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,41)
if
(
rc
!=
0
)
{
if
(
rc
!=
0
)
{
#else
if
(
rc
==
0
)
{
#endif
ocp_unregister_driver
(
&
gfar_driver
);
ocp_unregister_driver
(
&
gfar_driver
);
return
-
ENODEV
;
return
-
ENODEV
;
}
}
...
...
drivers/net/gianfar.h
View file @
f3c1e4cf
...
@@ -8,7 +8,7 @@
...
@@ -8,7 +8,7 @@
* Author: Andy Fleming
* Author: Andy Fleming
* Maintainer: Kumar Gala (kumar.gala@freescale.com)
* Maintainer: Kumar Gala (kumar.gala@freescale.com)
*
*
* Copyright
2004 Freescale Semiconductor, Inc
* Copyright
(c) 2002-2004 Freescale Semiconductor, Inc.
*
*
* This program is free software; you can redistribute it and/or modify it
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* under the terms of the GNU General Public License as published by the
...
@@ -17,6 +17,8 @@
...
@@ -17,6 +17,8 @@
*
*
* Still left to do:
* Still left to do:
* -Add support for module parameters
* -Add support for module parameters
* -Add support for ethtool -s
* -Add patch for ethtool phys id
*/
*/
#ifndef __GIANFAR_H
#ifndef __GIANFAR_H
#define __GIANFAR_H
#define __GIANFAR_H
...
@@ -42,15 +44,7 @@
...
@@ -42,15 +44,7 @@
#include <linux/module.h>
#include <linux/module.h>
#include <linux/version.h>
#include <linux/version.h>
#include <linux/crc32.h>
#include <linux/crc32.h>
#if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,41)
#include <linux/workqueue.h>
#include <linux/workqueue.h>
#else
#include <linux/tqueue.h>
#define work_struct tq_struct
#define schedule_work schedule_task
#endif
#include <linux/ethtool.h>
#include <linux/ethtool.h>
#include <linux/netdevice.h>
#include <linux/netdevice.h>
#include <asm/ocp.h>
#include <asm/ocp.h>
...
@@ -70,8 +64,13 @@
...
@@ -70,8 +64,13 @@
#define MAC_ADDR_LEN 6
#define MAC_ADDR_LEN 6
extern
char
gfar_driver_name
[];
#define PHY_INIT_TIMEOUT 100000
extern
char
gfar_driver_version
[];
#define GFAR_PHY_CHANGE_TIME 2
#define DEVICE_NAME "%s: Gianfar Ethernet Controller Version 1.1, "
#define DRV_NAME "gfar-enet"
extern
const
char
gfar_driver_name
[];
extern
const
char
gfar_driver_version
[];
/* These need to be powers of 2 for this driver */
/* These need to be powers of 2 for this driver */
#ifdef CONFIG_GFAR_NAPI
#ifdef CONFIG_GFAR_NAPI
...
@@ -105,11 +104,13 @@ extern char gfar_driver_version[];
...
@@ -105,11 +104,13 @@ extern char gfar_driver_version[];
#define GFAR_100_TIME 2560
#define GFAR_100_TIME 2560
#define GFAR_10_TIME 25600
#define GFAR_10_TIME 25600
#define DEFAULT_TX_COALESCE 1
#define DEFAULT_TXCOUNT 16
#define DEFAULT_TXCOUNT 16
#define DEFAULT_TXTIME
32768
#define DEFAULT_TXTIME
400
#define DEFAULT_RX_COALESCE 1
#define DEFAULT_RXCOUNT 16
#define DEFAULT_RXCOUNT 16
#define DEFAULT_RXTIME
32768
#define DEFAULT_RXTIME
400
#define TBIPA_VALUE 0x1f
#define TBIPA_VALUE 0x1f
#define MIIMCFG_INIT_VALUE 0x00000007
#define MIIMCFG_INIT_VALUE 0x00000007
...
@@ -467,8 +468,7 @@ struct gfar {
...
@@ -467,8 +468,7 @@ struct gfar {
* empty and completely full conditions. The empty/ready indicator in
* empty and completely full conditions. The empty/ready indicator in
* the buffer descriptor determines the actual condition.
* the buffer descriptor determines the actual condition.
*/
*/
struct
gfar_private
struct
gfar_private
{
{
/* pointers to arrays of skbuffs for tx and rx */
/* pointers to arrays of skbuffs for tx and rx */
struct
sk_buff
**
tx_skbuff
;
struct
sk_buff
**
tx_skbuff
;
struct
sk_buff
**
rx_skbuff
;
struct
sk_buff
**
rx_skbuff
;
...
@@ -496,7 +496,6 @@ struct gfar_private
...
@@ -496,7 +496,6 @@ struct gfar_private
struct
txbd8
*
cur_tx
;
/* Next free ring entry */
struct
txbd8
*
cur_tx
;
/* Next free ring entry */
struct
txbd8
*
dirty_tx
;
/* The Ring entry to be freed. */
struct
txbd8
*
dirty_tx
;
/* The Ring entry to be freed. */
struct
gfar
*
regs
;
/* Pointer to the GFAR memory mapped Registers */
struct
gfar
*
regs
;
/* Pointer to the GFAR memory mapped Registers */
struct
phy_info
*
phyinfo
;
struct
gfar
*
phyregs
;
struct
gfar
*
phyregs
;
struct
work_struct
tq
;
struct
work_struct
tq
;
struct
timer_list
phy_info_timer
;
struct
timer_list
phy_info_timer
;
...
@@ -509,15 +508,14 @@ struct gfar_private
...
@@ -509,15 +508,14 @@ struct gfar_private
unsigned
int
rx_ring_size
;
unsigned
int
rx_ring_size
;
wait_queue_head_t
rxcleanupq
;
wait_queue_head_t
rxcleanupq
;
unsigned
int
rxclean
;
unsigned
int
rxclean
;
int
link
;
/* current link state */
int
oldlink
;
int
duplexity
;
/* Indicates negotiated duplex state */
int
olddplx
;
int
speed
;
/* Indicates negotiated speed */
int
oldspeed
;
/* Info structure initialized by board setup code */
/* Info structure initialized by board setup code */
struct
ocp_gfar_data
*
einfo
;
struct
ocp_gfar_data
*
einfo
;
struct
gfar_mii_info
*
mii_info
;
int
oldspeed
;
int
oldduplex
;
int
oldlink
;
};
};
extern
inline
u32
gfar_read
(
volatile
unsigned
*
addr
)
extern
inline
u32
gfar_read
(
volatile
unsigned
*
addr
)
...
@@ -532,6 +530,6 @@ extern inline void gfar_write(volatile unsigned *addr, u32 val)
...
@@ -532,6 +530,6 @@ extern inline void gfar_write(volatile unsigned *addr, u32 val)
out_be32
(
addr
,
val
);
out_be32
(
addr
,
val
);
}
}
extern
struct
ethtool_ops
*
gfar_op_array
[];
#endif
/* __GIANFAR_H */
#endif
/* __GIANFAR_H */
drivers/net/gianfar_ethtool.c
View file @
f3c1e4cf
/*
/*
* drivers/net/gianfar_ethtool.c
*
drivers/net/gianfar_ethtool.c
*
*
* Gianfar Ethernet Driver
*
Gianfar Ethernet Driver
* Ethtool support for Gianfar Enet
*
Ethtool support for Gianfar Enet
* Based on e1000 ethtool support
*
Based on e1000 ethtool support
*
*
* Author: Andy Fleming
*
Author: Andy Fleming
* Maintainer: Kumar Gala (kumar.gala@freescale.com)
*
Maintainer: Kumar Gala (kumar.gala@freescale.com)
*
*
*
Copyright 2004 Freescale Semiconductor, Inc
*
Copyright (c) 2003,2004 Freescale Semiconductor, Inc.
*
*
* This software may be used and distributed according to
*
This software may be used and distributed according to
* the terms of the GNU Public License, Version 2, incorporated herein
*
the terms of the GNU Public License, Version 2, incorporated herein
* by reference.
*
by reference.
*/
*/
#include <linux/config.h>
#include <linux/config.h>
...
@@ -58,64 +58,64 @@ int gfar_sringparam(struct net_device *dev, struct ethtool_ringparam *rvals);
...
@@ -58,64 +58,64 @@ int gfar_sringparam(struct net_device *dev, struct ethtool_ringparam *rvals);
void
gfar_gdrvinfo
(
struct
net_device
*
dev
,
struct
ethtool_drvinfo
*
drvinfo
);
void
gfar_gdrvinfo
(
struct
net_device
*
dev
,
struct
ethtool_drvinfo
*
drvinfo
);
static
char
stat_gstrings
[][
ETH_GSTRING_LEN
]
=
{
static
char
stat_gstrings
[][
ETH_GSTRING_LEN
]
=
{
"
RX Dropped by K
ernel"
,
"
rx-dropped-by-k
ernel"
,
"
RX Large Frame E
rrors"
,
"
rx-large-frame-e
rrors"
,
"
RX Short Frame E
rrors"
,
"
rx-short-frame-e
rrors"
,
"
RX Non-Octet E
rrors"
,
"
rx-non-octet-e
rrors"
,
"
RX CRC E
rrors"
,
"
rx-crc-e
rrors"
,
"
RX Overrun E
rrors"
,
"
rx-overrun-e
rrors"
,
"
RX Busy E
rrors"
,
"
rx-busy-e
rrors"
,
"
RX Babbling E
rrors"
,
"
rx-babbling-e
rrors"
,
"
RX Truncated F
rames"
,
"
rx-truncated-f
rames"
,
"
Ethernet Bus E
rror"
,
"
ethernet-bus-e
rror"
,
"
TX Babbling E
rrors"
,
"
tx-babbling-e
rrors"
,
"
TX Underrun E
rrors"
,
"
tx-underrun-e
rrors"
,
"
RX SKB Missing E
rrors"
,
"
rx-skb-missing-e
rrors"
,
"
TX Timeout E
rrors"
,
"
tx-timeout-e
rrors"
,
"tx
&rx 64B
frames"
,
"tx
-rx-64-
frames"
,
"tx
&rx 65-127B
frames"
,
"tx
-rx-65-127-
frames"
,
"tx
&rx 128-255B
frames"
,
"tx
-rx-128-255-
frames"
,
"tx
&rx 256-511B
frames"
,
"tx
-rx-256-511-
frames"
,
"tx
&rx 512-1023B
frames"
,
"tx
-rx-512-1023-
frames"
,
"tx
&rx 1024-1518B
frames"
,
"tx
-rx-1024-1518-
frames"
,
"tx
&rx 1519-1522B Good VLAN
"
,
"tx
-rx-1519-1522-good-vlan
"
,
"
RX
bytes"
,
"
rx-
bytes"
,
"
RX P
ackets"
,
"
rx-p
ackets"
,
"
RX FCS E
rrors"
,
"
rx-fcs-e
rrors"
,
"
Receive Multicast P
acket"
,
"
receive-multicast-p
acket"
,
"
Receive Broadcast P
acket"
,
"
receive-broadcast-p
acket"
,
"
RX Control Frame P
ackets"
,
"
rx-control-frame-p
ackets"
,
"
RX Pause Frame P
ackets"
,
"
rx-pause-frame-p
ackets"
,
"
RX Unknown OP C
ode"
,
"
rx-unknown-op-c
ode"
,
"
RX Alignment E
rror"
,
"
rx-alignment-e
rror"
,
"
RX Frame Length E
rror"
,
"
rx-frame-length-e
rror"
,
"
RX Code E
rror"
,
"
rx-code-e
rror"
,
"
RX Carrier Sense E
rror"
,
"
rx-carrier-sense-e
rror"
,
"
RX Undersize P
ackets"
,
"
rx-undersize-p
ackets"
,
"
RX Oversize P
ackets"
,
"
rx-oversize-p
ackets"
,
"
RX Fragmented F
rames"
,
"
rx-fragmented-f
rames"
,
"
RX Jabber F
rames"
,
"
rx-jabber-f
rames"
,
"
RX Dropped F
rames"
,
"
rx-dropped-f
rames"
,
"
TX Byte C
ounter"
,
"
tx-byte-c
ounter"
,
"
TX P
ackets"
,
"
tx-p
ackets"
,
"
TX Multicast P
ackets"
,
"
tx-multicast-p
ackets"
,
"
TX Broadcast P
ackets"
,
"
tx-broadcast-p
ackets"
,
"
TX Pause Control F
rames"
,
"
tx-pause-control-f
rames"
,
"
TX Deferral P
ackets"
,
"
tx-deferral-p
ackets"
,
"
TX Excessive Deferral P
ackets"
,
"
tx-excessive-deferral-p
ackets"
,
"
TX Single Collision P
ackets"
,
"
tx-single-collision-p
ackets"
,
"
TX Multiple Collision P
ackets"
,
"
tx-multiple-collision-p
ackets"
,
"
TX Late Collision P
ackets"
,
"
tx-late-collision-p
ackets"
,
"
TX Excessive Collision P
ackets"
,
"
tx-excessive-collision-p
ackets"
,
"
TX Total C
ollision"
,
"
tx-total-c
ollision"
,
"
RESERVED
"
,
"
reserved
"
,
"
TX Dropped F
rames"
,
"
tx-dropped-f
rames"
,
"
TX Jabber F
rames"
,
"
tx-jabber-f
rames"
,
"
TX FCS E
rrors"
,
"
tx-fcs-e
rrors"
,
"
TX Control F
rames"
,
"
tx-control-f
rames"
,
"
TX Oversize F
rames"
,
"
tx-oversize-f
rames"
,
"
TX Undersize F
rames"
,
"
tx-undersize-f
rames"
,
"
TX Fragmented F
rames"
,
"
tx-fragmented-f
rames"
,
};
};
/* Fill in an array of 64-bit statistics from various sources.
/* Fill in an array of 64-bit statistics from various sources.
...
@@ -125,7 +125,7 @@ static char stat_gstrings[][ETH_GSTRING_LEN] = {
...
@@ -125,7 +125,7 @@ static char stat_gstrings[][ETH_GSTRING_LEN] = {
void
gfar_fill_stats
(
struct
net_device
*
dev
,
struct
ethtool_stats
*
dummy
,
u64
*
buf
)
void
gfar_fill_stats
(
struct
net_device
*
dev
,
struct
ethtool_stats
*
dummy
,
u64
*
buf
)
{
{
int
i
;
int
i
;
struct
gfar_private
*
priv
=
(
struct
gfar_private
*
)
dev
->
priv
;
struct
gfar_private
*
priv
=
netdev_priv
(
dev
)
;
u32
*
rmon
=
(
u32
*
)
&
priv
->
regs
->
rmon
;
u32
*
rmon
=
(
u32
*
)
&
priv
->
regs
->
rmon
;
u64
*
extra
=
(
u64
*
)
&
priv
->
extra_stats
;
u64
*
extra
=
(
u64
*
)
&
priv
->
extra_stats
;
struct
gfar_stats
*
stats
=
(
struct
gfar_stats
*
)
buf
;
struct
gfar_stats
*
stats
=
(
struct
gfar_stats
*
)
buf
;
...
@@ -154,7 +154,7 @@ void gfar_fill_stats_normon(struct net_device *dev,
...
@@ -154,7 +154,7 @@ void gfar_fill_stats_normon(struct net_device *dev,
struct
ethtool_stats
*
dummy
,
u64
*
buf
)
struct
ethtool_stats
*
dummy
,
u64
*
buf
)
{
{
int
i
;
int
i
;
struct
gfar_private
*
priv
=
(
struct
gfar_private
*
)
dev
->
priv
;
struct
gfar_private
*
priv
=
netdev_priv
(
dev
)
;
u64
*
extra
=
(
u64
*
)
&
priv
->
extra_stats
;
u64
*
extra
=
(
u64
*
)
&
priv
->
extra_stats
;
for
(
i
=
0
;
i
<
GFAR_EXTRA_STATS_LEN
;
i
++
)
{
for
(
i
=
0
;
i
<
GFAR_EXTRA_STATS_LEN
;
i
++
)
{
...
@@ -171,7 +171,7 @@ int gfar_stats_count_normon(struct net_device *dev)
...
@@ -171,7 +171,7 @@ int gfar_stats_count_normon(struct net_device *dev)
void
gfar_gdrvinfo
(
struct
net_device
*
dev
,
struct
void
gfar_gdrvinfo
(
struct
net_device
*
dev
,
struct
ethtool_drvinfo
*
drvinfo
)
ethtool_drvinfo
*
drvinfo
)
{
{
strncpy
(
drvinfo
->
driver
,
gfar_driver_name
,
GFAR_INFOSTR_LEN
);
strncpy
(
drvinfo
->
driver
,
DRV_NAME
,
GFAR_INFOSTR_LEN
);
strncpy
(
drvinfo
->
version
,
gfar_driver_version
,
GFAR_INFOSTR_LEN
);
strncpy
(
drvinfo
->
version
,
gfar_driver_version
,
GFAR_INFOSTR_LEN
);
strncpy
(
drvinfo
->
fw_version
,
"N/A"
,
GFAR_INFOSTR_LEN
);
strncpy
(
drvinfo
->
fw_version
,
"N/A"
,
GFAR_INFOSTR_LEN
);
strncpy
(
drvinfo
->
bus_info
,
"N/A"
,
GFAR_INFOSTR_LEN
);
strncpy
(
drvinfo
->
bus_info
,
"N/A"
,
GFAR_INFOSTR_LEN
);
...
@@ -184,7 +184,7 @@ void gfar_gdrvinfo(struct net_device *dev, struct
...
@@ -184,7 +184,7 @@ void gfar_gdrvinfo(struct net_device *dev, struct
/* Return the current settings in the ethtool_cmd structure */
/* Return the current settings in the ethtool_cmd structure */
int
gfar_gsettings
(
struct
net_device
*
dev
,
struct
ethtool_cmd
*
cmd
)
int
gfar_gsettings
(
struct
net_device
*
dev
,
struct
ethtool_cmd
*
cmd
)
{
{
struct
gfar_private
*
priv
=
(
struct
gfar_private
*
)
dev
->
priv
;
struct
gfar_private
*
priv
=
netdev_priv
(
dev
)
;
uint
gigabit_support
=
uint
gigabit_support
=
priv
->
einfo
->
flags
&
GFAR_HAS_GIGABIT
?
SUPPORTED_1000baseT_Full
:
0
;
priv
->
einfo
->
flags
&
GFAR_HAS_GIGABIT
?
SUPPORTED_1000baseT_Full
:
0
;
uint
gigabit_advert
=
uint
gigabit_advert
=
...
@@ -201,10 +201,10 @@ int gfar_gsettings(struct net_device *dev, struct ethtool_cmd *cmd)
...
@@ -201,10 +201,10 @@ int gfar_gsettings(struct net_device *dev, struct ethtool_cmd *cmd)
|
ADVERTISED_100baseT_Full
|
ADVERTISED_100baseT_Full
|
gigabit_advert
|
ADVERTISED_Autoneg
);
|
gigabit_advert
|
ADVERTISED_Autoneg
);
cmd
->
speed
=
priv
->
speed
;
cmd
->
speed
=
priv
->
mii_info
->
speed
;
cmd
->
duplex
=
priv
->
duplexity
;
cmd
->
duplex
=
priv
->
mii_info
->
duplex
;
cmd
->
port
=
PORT_MII
;
cmd
->
port
=
PORT_MII
;
cmd
->
phy_address
=
priv
->
einfo
->
phy
id
;
cmd
->
phy_address
=
priv
->
mii_info
->
mii_
id
;
cmd
->
transceiver
=
XCVR_EXTERNAL
;
cmd
->
transceiver
=
XCVR_EXTERNAL
;
cmd
->
autoneg
=
AUTONEG_ENABLE
;
cmd
->
autoneg
=
AUTONEG_ENABLE
;
cmd
->
maxtxpkt
=
priv
->
txcount
;
cmd
->
maxtxpkt
=
priv
->
txcount
;
...
@@ -223,7 +223,7 @@ int gfar_reglen(struct net_device *dev)
...
@@ -223,7 +223,7 @@ int gfar_reglen(struct net_device *dev)
void
gfar_get_regs
(
struct
net_device
*
dev
,
struct
ethtool_regs
*
regs
,
void
*
regbuf
)
void
gfar_get_regs
(
struct
net_device
*
dev
,
struct
ethtool_regs
*
regs
,
void
*
regbuf
)
{
{
int
i
;
int
i
;
struct
gfar_private
*
priv
=
(
struct
gfar_private
*
)
dev
->
priv
;
struct
gfar_private
*
priv
=
netdev_priv
(
dev
)
;
u32
*
theregs
=
(
u32
*
)
priv
->
regs
;
u32
*
theregs
=
(
u32
*
)
priv
->
regs
;
u32
*
buf
=
(
u32
*
)
regbuf
;
u32
*
buf
=
(
u32
*
)
regbuf
;
...
@@ -231,13 +231,6 @@ void gfar_get_regs(struct net_device *dev, struct ethtool_regs *regs, void *regb
...
@@ -231,13 +231,6 @@ void gfar_get_regs(struct net_device *dev, struct ethtool_regs *regs, void *regb
buf
[
i
]
=
theregs
[
i
];
buf
[
i
]
=
theregs
[
i
];
}
}
/* Return the link state 1 is up, 0 is down */
u32
gfar_get_link
(
struct
net_device
*
dev
)
{
struct
gfar_private
*
priv
=
(
struct
gfar_private
*
)
dev
->
priv
;
return
(
u32
)
priv
->
link
;
}
/* Fill in a buffer with the strings which correspond to the
/* Fill in a buffer with the strings which correspond to the
* stats */
* stats */
void
gfar_gstrings
(
struct
net_device
*
dev
,
u32
stringset
,
u8
*
buf
)
void
gfar_gstrings
(
struct
net_device
*
dev
,
u32
stringset
,
u8
*
buf
)
...
@@ -252,7 +245,7 @@ static unsigned int gfar_usecs2ticks(struct gfar_private *priv, unsigned int use
...
@@ -252,7 +245,7 @@ static unsigned int gfar_usecs2ticks(struct gfar_private *priv, unsigned int use
unsigned
int
count
;
unsigned
int
count
;
/* The timer is different, depending on the interface speed */
/* The timer is different, depending on the interface speed */
switch
(
priv
->
speed
)
{
switch
(
priv
->
mii_info
->
speed
)
{
case
1000
:
case
1000
:
count
=
GFAR_GBIT_TIME
;
count
=
GFAR_GBIT_TIME
;
break
;
break
;
...
@@ -276,7 +269,7 @@ static unsigned int gfar_ticks2usecs(struct gfar_private *priv, unsigned int tic
...
@@ -276,7 +269,7 @@ static unsigned int gfar_ticks2usecs(struct gfar_private *priv, unsigned int tic
unsigned
int
count
;
unsigned
int
count
;
/* The timer is different, depending on the interface speed */
/* The timer is different, depending on the interface speed */
switch
(
priv
->
speed
)
{
switch
(
priv
->
mii_info
->
speed
)
{
case
1000
:
case
1000
:
count
=
GFAR_GBIT_TIME
;
count
=
GFAR_GBIT_TIME
;
break
;
break
;
...
@@ -298,7 +291,7 @@ static unsigned int gfar_ticks2usecs(struct gfar_private *priv, unsigned int tic
...
@@ -298,7 +291,7 @@ static unsigned int gfar_ticks2usecs(struct gfar_private *priv, unsigned int tic
* structure. */
* structure. */
int
gfar_gcoalesce
(
struct
net_device
*
dev
,
struct
ethtool_coalesce
*
cvals
)
int
gfar_gcoalesce
(
struct
net_device
*
dev
,
struct
ethtool_coalesce
*
cvals
)
{
{
struct
gfar_private
*
priv
=
(
struct
gfar_private
*
)
dev
->
priv
;
struct
gfar_private
*
priv
=
netdev_priv
(
dev
)
;
cvals
->
rx_coalesce_usecs
=
gfar_ticks2usecs
(
priv
,
priv
->
rxtime
);
cvals
->
rx_coalesce_usecs
=
gfar_ticks2usecs
(
priv
,
priv
->
rxtime
);
cvals
->
rx_max_coalesced_frames
=
priv
->
rxcount
;
cvals
->
rx_max_coalesced_frames
=
priv
->
rxcount
;
...
@@ -344,7 +337,7 @@ int gfar_gcoalesce(struct net_device *dev, struct ethtool_coalesce *cvals)
...
@@ -344,7 +337,7 @@ int gfar_gcoalesce(struct net_device *dev, struct ethtool_coalesce *cvals)
*/
*/
int
gfar_scoalesce
(
struct
net_device
*
dev
,
struct
ethtool_coalesce
*
cvals
)
int
gfar_scoalesce
(
struct
net_device
*
dev
,
struct
ethtool_coalesce
*
cvals
)
{
{
struct
gfar_private
*
priv
=
(
struct
gfar_private
*
)
dev
->
priv
;
struct
gfar_private
*
priv
=
netdev_priv
(
dev
)
;
/* Set up rx coalescing */
/* Set up rx coalescing */
if
((
cvals
->
rx_coalesce_usecs
==
0
)
||
if
((
cvals
->
rx_coalesce_usecs
==
0
)
||
...
@@ -386,7 +379,7 @@ int gfar_scoalesce(struct net_device *dev, struct ethtool_coalesce *cvals)
...
@@ -386,7 +379,7 @@ int gfar_scoalesce(struct net_device *dev, struct ethtool_coalesce *cvals)
* jumbo are ignored by the driver */
* jumbo are ignored by the driver */
void
gfar_gringparam
(
struct
net_device
*
dev
,
struct
ethtool_ringparam
*
rvals
)
void
gfar_gringparam
(
struct
net_device
*
dev
,
struct
ethtool_ringparam
*
rvals
)
{
{
struct
gfar_private
*
priv
=
(
struct
gfar_private
*
)
dev
->
priv
;
struct
gfar_private
*
priv
=
netdev_priv
(
dev
)
;
rvals
->
rx_max_pending
=
GFAR_RX_MAX_RING_SIZE
;
rvals
->
rx_max_pending
=
GFAR_RX_MAX_RING_SIZE
;
rvals
->
rx_mini_max_pending
=
GFAR_RX_MAX_RING_SIZE
;
rvals
->
rx_mini_max_pending
=
GFAR_RX_MAX_RING_SIZE
;
...
@@ -409,7 +402,7 @@ void gfar_gringparam(struct net_device *dev, struct ethtool_ringparam *rvals)
...
@@ -409,7 +402,7 @@ void gfar_gringparam(struct net_device *dev, struct ethtool_ringparam *rvals)
int
gfar_sringparam
(
struct
net_device
*
dev
,
struct
ethtool_ringparam
*
rvals
)
int
gfar_sringparam
(
struct
net_device
*
dev
,
struct
ethtool_ringparam
*
rvals
)
{
{
u32
tempval
;
u32
tempval
;
struct
gfar_private
*
priv
=
(
struct
gfar_private
*
)
dev
->
priv
;
struct
gfar_private
*
priv
=
netdev_priv
(
dev
)
;
int
err
=
0
;
int
err
=
0
;
if
(
rvals
->
rx_pending
>
GFAR_RX_MAX_RING_SIZE
)
if
(
rvals
->
rx_pending
>
GFAR_RX_MAX_RING_SIZE
)
...
@@ -473,7 +466,7 @@ struct ethtool_ops gfar_ethtool_ops = {
...
@@ -473,7 +466,7 @@ struct ethtool_ops gfar_ethtool_ops = {
.
get_drvinfo
=
gfar_gdrvinfo
,
.
get_drvinfo
=
gfar_gdrvinfo
,
.
get_regs_len
=
gfar_reglen
,
.
get_regs_len
=
gfar_reglen
,
.
get_regs
=
gfar_get_regs
,
.
get_regs
=
gfar_get_regs
,
.
get_link
=
gfar
_get_link
,
.
get_link
=
ethtool_op
_get_link
,
.
get_coalesce
=
gfar_gcoalesce
,
.
get_coalesce
=
gfar_gcoalesce
,
.
set_coalesce
=
gfar_scoalesce
,
.
set_coalesce
=
gfar_scoalesce
,
.
get_ringparam
=
gfar_gringparam
,
.
get_ringparam
=
gfar_gringparam
,
...
@@ -482,3 +475,51 @@ struct ethtool_ops gfar_ethtool_ops = {
...
@@ -482,3 +475,51 @@ struct ethtool_ops gfar_ethtool_ops = {
.
get_stats_count
=
gfar_stats_count
,
.
get_stats_count
=
gfar_stats_count
,
.
get_ethtool_stats
=
gfar_fill_stats
,
.
get_ethtool_stats
=
gfar_fill_stats
,
};
};
struct
ethtool_ops
gfar_normon_nocoalesce_ethtool_ops
=
{
.
get_settings
=
gfar_gsettings
,
.
get_drvinfo
=
gfar_gdrvinfo
,
.
get_regs_len
=
gfar_reglen
,
.
get_regs
=
gfar_get_regs
,
.
get_link
=
ethtool_op_get_link
,
.
get_ringparam
=
gfar_gringparam
,
.
set_ringparam
=
gfar_sringparam
,
.
get_strings
=
gfar_gstrings_normon
,
.
get_stats_count
=
gfar_stats_count_normon
,
.
get_ethtool_stats
=
gfar_fill_stats_normon
,
};
struct
ethtool_ops
gfar_nocoalesce_ethtool_ops
=
{
.
get_settings
=
gfar_gsettings
,
.
get_drvinfo
=
gfar_gdrvinfo
,
.
get_regs_len
=
gfar_reglen
,
.
get_regs
=
gfar_get_regs
,
.
get_link
=
ethtool_op_get_link
,
.
get_ringparam
=
gfar_gringparam
,
.
set_ringparam
=
gfar_sringparam
,
.
get_strings
=
gfar_gstrings
,
.
get_stats_count
=
gfar_stats_count
,
.
get_ethtool_stats
=
gfar_fill_stats
,
};
struct
ethtool_ops
gfar_normon_ethtool_ops
=
{
.
get_settings
=
gfar_gsettings
,
.
get_drvinfo
=
gfar_gdrvinfo
,
.
get_regs_len
=
gfar_reglen
,
.
get_regs
=
gfar_get_regs
,
.
get_link
=
ethtool_op_get_link
,
.
get_coalesce
=
gfar_gcoalesce
,
.
set_coalesce
=
gfar_scoalesce
,
.
get_ringparam
=
gfar_gringparam
,
.
set_ringparam
=
gfar_sringparam
,
.
get_strings
=
gfar_gstrings_normon
,
.
get_stats_count
=
gfar_stats_count_normon
,
.
get_ethtool_stats
=
gfar_fill_stats_normon
,
};
struct
ethtool_ops
*
gfar_op_array
[]
=
{
&
gfar_ethtool_ops
,
&
gfar_normon_ethtool_ops
,
&
gfar_nocoalesce_ethtool_ops
,
&
gfar_normon_nocoalesce_ethtool_ops
};
drivers/net/gianfar_phy.c
View file @
f3c1e4cf
...
@@ -8,7 +8,7 @@
...
@@ -8,7 +8,7 @@
* Author: Andy Fleming
* Author: Andy Fleming
* Maintainer: Kumar Gala (kumar.gala@freescale.com)
* Maintainer: Kumar Gala (kumar.gala@freescale.com)
*
*
* Copyright
2004 Freescale Semiconductor, Inc
* Copyright
(c) 2002-2004 Freescale Semiconductor, Inc.
*
*
* This program is free software; you can redistribute it and/or modify it
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* under the terms of the GNU General Public License as published by the
...
@@ -38,21 +38,31 @@
...
@@ -38,21 +38,31 @@
#include <linux/module.h>
#include <linux/module.h>
#include <linux/version.h>
#include <linux/version.h>
#include <linux/crc32.h>
#include <linux/crc32.h>
#include <linux/mii.h>
#include "gianfar.h"
#include "gianfar.h"
#include "gianfar_phy.h"
#include "gianfar_phy.h"
static
void
config_genmii_advert
(
struct
gfar_mii_info
*
mii_info
);
static
void
genmii_setup_forced
(
struct
gfar_mii_info
*
mii_info
);
static
void
genmii_restart_aneg
(
struct
gfar_mii_info
*
mii_info
);
static
int
gbit_config_aneg
(
struct
gfar_mii_info
*
mii_info
);
static
int
genmii_config_aneg
(
struct
gfar_mii_info
*
mii_info
);
static
int
genmii_update_link
(
struct
gfar_mii_info
*
mii_info
);
static
int
genmii_read_status
(
struct
gfar_mii_info
*
mii_info
);
u16
phy_read
(
struct
gfar_mii_info
*
mii_info
,
u16
regnum
);
void
phy_write
(
struct
gfar_mii_info
*
mii_info
,
u16
regnum
,
u16
val
);
/* Write value to the PHY for this device to the register at regnum, */
/* Write value to the PHY for this device to the register at regnum, */
/* waiting until the write is done before it returns. All PHY */
/* waiting until the write is done before it returns. All PHY */
/* configuration has to be done through the TSEC1 MIIM regs */
/* configuration has to be done through the TSEC1 MIIM regs */
void
write_phy_reg
(
struct
net_device
*
dev
,
u16
regnum
,
u16
value
)
void
write_phy_reg
(
struct
net_device
*
dev
,
int
mii_id
,
int
regnum
,
int
value
)
{
{
struct
gfar_private
*
priv
=
(
struct
gfar_private
*
)
dev
->
priv
;
struct
gfar_private
*
priv
=
netdev_priv
(
dev
)
;
struct
gfar
*
regbase
=
priv
->
phyregs
;
struct
gfar
*
regbase
=
priv
->
phyregs
;
struct
ocp_gfar_data
*
einfo
=
priv
->
einfo
;
/* Set the PHY address and the register address we want to write */
/* Set the PHY address and the register address we want to write */
gfar_write
(
&
regbase
->
miimadd
,
(
(
einfo
->
phyid
)
<<
8
)
|
regnum
);
gfar_write
(
&
regbase
->
miimadd
,
(
mii_id
<<
8
)
|
regnum
);
/* Write out the value we want */
/* Write out the value we want */
gfar_write
(
&
regbase
->
miimcon
,
value
);
gfar_write
(
&
regbase
->
miimcon
,
value
);
...
@@ -65,19 +75,18 @@ void write_phy_reg(struct net_device *dev, u16 regnum, u16 value)
...
@@ -65,19 +75,18 @@ void write_phy_reg(struct net_device *dev, u16 regnum, u16 value)
/* Reads from register regnum in the PHY for device dev, */
/* Reads from register regnum in the PHY for device dev, */
/* returning the value. Clears miimcom first. All PHY */
/* returning the value. Clears miimcom first. All PHY */
/* configuration has to be done through the TSEC1 MIIM regs */
/* configuration has to be done through the TSEC1 MIIM regs */
u16
read_phy_reg
(
struct
net_device
*
dev
,
u16
regnum
)
int
read_phy_reg
(
struct
net_device
*
dev
,
int
mii_id
,
int
regnum
)
{
{
struct
gfar_private
*
priv
=
(
struct
gfar_private
*
)
dev
->
priv
;
struct
gfar_private
*
priv
=
netdev_priv
(
dev
)
;
struct
gfar
*
regbase
=
priv
->
phyregs
;
struct
gfar
*
regbase
=
priv
->
phyregs
;
struct
ocp_gfar_data
*
einfo
=
priv
->
einfo
;
u16
value
;
u16
value
;
/* Set the PHY address and the register address we want to read */
/* Set the PHY address and the register address we want to read */
gfar_write
(
&
regbase
->
miimadd
,
(
(
einfo
->
phyid
)
<<
8
)
|
regnum
);
gfar_write
(
&
regbase
->
miimadd
,
(
mii_id
<<
8
)
|
regnum
);
/* Clear miimcom, and then initiate a read */
/* Clear miimcom, and then initiate a read */
gfar_write
(
&
regbase
->
miimcom
,
0
);
gfar_write
(
&
regbase
->
miimcom
,
0
);
gfar_write
(
&
regbase
->
miimcom
,
MII
M
_READ_COMMAND
);
gfar_write
(
&
regbase
->
miimcom
,
MII_READ_COMMAND
);
/* Wait for the transaction to finish */
/* Wait for the transaction to finish */
while
(
gfar_read
(
&
regbase
->
miimind
)
&
(
MIIMIND_NOTVALID
|
MIIMIND_BUSY
))
while
(
gfar_read
(
&
regbase
->
miimind
)
&
(
MIIMIND_NOTVALID
|
MIIMIND_BUSY
))
...
@@ -89,362 +98,557 @@ u16 read_phy_reg(struct net_device *dev, u16 regnum)
...
@@ -89,362 +98,557 @@ u16 read_phy_reg(struct net_device *dev, u16 regnum)
return
value
;
return
value
;
}
}
/* returns which value to write to the control register. */
void
mii_clear_phy_interrupt
(
struct
gfar_mii_info
*
mii_info
)
/* For 10/100 the value is slightly different. */
u16
mii_cr_init
(
u16
mii_reg
,
struct
net_device
*
dev
)
{
{
struct
gfar_private
*
priv
=
(
struct
gfar_private
*
)
dev
->
priv
;
if
(
mii_info
->
phyinfo
->
ack_interrupt
)
struct
ocp_gfar_data
*
einfo
=
priv
->
einfo
;
mii_info
->
phyinfo
->
ack_interrupt
(
mii_info
);
}
if
(
einfo
->
flags
&
GFAR_HAS_GIGABIT
)
return
MIIM_CONTROL_INIT
;
void
mii_configure_phy_interrupt
(
struct
gfar_mii_info
*
mii_info
,
u32
interrupts
)
else
{
return
MIIM_CR_INIT
;
mii_info
->
interrupts
=
interrupts
;
if
(
mii_info
->
phyinfo
->
config_intr
)
mii_info
->
phyinfo
->
config_intr
(
mii_info
);
}
/* Writes MII_ADVERTISE with the appropriate values, after
* sanitizing advertise to make sure only supported features
* are advertised
*/
static
void
config_genmii_advert
(
struct
gfar_mii_info
*
mii_info
)
{
u32
advertise
;
u16
adv
;
/* Only allow advertising what this PHY supports */
mii_info
->
advertising
&=
mii_info
->
phyinfo
->
features
;
advertise
=
mii_info
->
advertising
;
/* Setup standard advertisement */
adv
=
phy_read
(
mii_info
,
MII_ADVERTISE
);
adv
&=
~
(
ADVERTISE_ALL
|
ADVERTISE_100BASE4
);
if
(
advertise
&
ADVERTISED_10baseT_Half
)
adv
|=
ADVERTISE_10HALF
;
if
(
advertise
&
ADVERTISED_10baseT_Full
)
adv
|=
ADVERTISE_10FULL
;
if
(
advertise
&
ADVERTISED_100baseT_Half
)
adv
|=
ADVERTISE_100HALF
;
if
(
advertise
&
ADVERTISED_100baseT_Full
)
adv
|=
ADVERTISE_100FULL
;
phy_write
(
mii_info
,
MII_ADVERTISE
,
adv
);
}
}
#define BRIEF_GFAR_ERRORS
static
void
genmii_setup_forced
(
struct
gfar_mii_info
*
mii_info
)
/* Wait for auto-negotiation to complete */
u16
mii_parse_sr
(
u16
mii_reg
,
struct
net_device
*
dev
)
{
{
struct
gfar_private
*
priv
=
(
struct
gfar_private
*
)
dev
->
priv
;
u16
ctrl
;
u32
features
=
mii_info
->
phyinfo
->
features
;
ctrl
=
phy_read
(
mii_info
,
MII_BMCR
);
ctrl
&=
~
(
BMCR_FULLDPLX
|
BMCR_SPEED100
|
BMCR_SPEED1000
|
BMCR_ANENABLE
);
ctrl
|=
BMCR_RESET
;
switch
(
mii_info
->
speed
)
{
case
SPEED_1000
:
if
(
features
&
(
SUPPORTED_1000baseT_Half
|
SUPPORTED_1000baseT_Full
))
{
ctrl
|=
BMCR_SPEED1000
;
break
;
}
mii_info
->
speed
=
SPEED_100
;
case
SPEED_100
:
if
(
features
&
(
SUPPORTED_100baseT_Half
|
SUPPORTED_100baseT_Full
))
{
ctrl
|=
BMCR_SPEED100
;
break
;
}
mii_info
->
speed
=
SPEED_10
;
case
SPEED_10
:
if
(
features
&
(
SUPPORTED_10baseT_Half
|
SUPPORTED_10baseT_Full
))
break
;
default:
/* Unsupported speed! */
printk
(
KERN_ERR
"%s: Bad speed!
\n
"
,
mii_info
->
dev
->
name
);
break
;
}
unsigned
int
timeout
=
GFAR_AN_TIMEOUT
;
phy_write
(
mii_info
,
MII_BMCR
,
ctrl
);
}
if
(
mii_reg
&
MIIM_STATUS_LINK
)
priv
->
link
=
1
;
else
priv
->
link
=
0
;
/* Only auto-negotiate if the link has just gone up
*/
/* Enable and Restart Autonegotiation
*/
if
(
priv
->
link
&&
!
priv
->
oldlink
)
{
static
void
genmii_restart_aneg
(
struct
gfar_mii_info
*
mii_info
)
while
((
!
(
mii_reg
&
MIIM_STATUS_AN_DONE
))
&&
timeout
--
)
{
mii_reg
=
read_phy_reg
(
dev
,
MIIM_STATUS
)
;
u16
ctl
;
#if defined(BRIEF_GFAR_ERRORS)
ctl
=
phy_read
(
mii_info
,
MII_BMCR
);
if
(
mii_reg
&
MIIM_STATUS_AN_DONE
)
ctl
|=
(
BMCR_ANENABLE
|
BMCR_ANRESTART
);
printk
(
KERN_INFO
"%s: Auto-negotiation done
\n
"
,
phy_write
(
mii_info
,
MII_BMCR
,
ctl
);
dev
->
name
);
}
else
printk
(
KERN_INFO
"%s: Auto-negotiation timed out
\n
"
,
dev
->
name
);
static
int
gbit_config_aneg
(
struct
gfar_mii_info
*
mii_info
)
#endif
{
}
u16
adv
;
u32
advertise
;
if
(
mii_info
->
autoneg
)
{
/* Configure the ADVERTISE register */
config_genmii_advert
(
mii_info
);
advertise
=
mii_info
->
advertising
;
adv
=
phy_read
(
mii_info
,
MII_1000BASETCONTROL
);
adv
&=
~
(
MII_1000BASETCONTROL_FULLDUPLEXCAP
|
MII_1000BASETCONTROL_HALFDUPLEXCAP
);
if
(
advertise
&
SUPPORTED_1000baseT_Half
)
adv
|=
MII_1000BASETCONTROL_HALFDUPLEXCAP
;
if
(
advertise
&
SUPPORTED_1000baseT_Full
)
adv
|=
MII_1000BASETCONTROL_FULLDUPLEXCAP
;
phy_write
(
mii_info
,
MII_1000BASETCONTROL
,
adv
);
/* Start/Restart aneg */
genmii_restart_aneg
(
mii_info
);
}
else
genmii_setup_forced
(
mii_info
);
return
0
;
}
static
int
marvell_config_aneg
(
struct
gfar_mii_info
*
mii_info
)
{
/* The Marvell PHY has an errata which requires
* that certain registers get written in order
* to restart autonegotiation */
phy_write
(
mii_info
,
MII_BMCR
,
BMCR_RESET
);
phy_write
(
mii_info
,
0x1d
,
0x1f
);
phy_write
(
mii_info
,
0x1e
,
0x200c
);
phy_write
(
mii_info
,
0x1d
,
0x5
);
phy_write
(
mii_info
,
0x1e
,
0
);
phy_write
(
mii_info
,
0x1e
,
0x100
);
gbit_config_aneg
(
mii_info
);
return
0
;
}
static
int
genmii_config_aneg
(
struct
gfar_mii_info
*
mii_info
)
{
if
(
mii_info
->
autoneg
)
{
config_genmii_advert
(
mii_info
);
genmii_restart_aneg
(
mii_info
);
}
else
genmii_setup_forced
(
mii_info
);
return
0
;
}
static
int
genmii_update_link
(
struct
gfar_mii_info
*
mii_info
)
{
u16
status
;
/* Do a fake read */
phy_read
(
mii_info
,
MII_BMSR
);
/* Read link and autonegotiation status */
status
=
phy_read
(
mii_info
,
MII_BMSR
);
if
((
status
&
BMSR_LSTATUS
)
==
0
)
mii_info
->
link
=
0
;
else
mii_info
->
link
=
1
;
/* If we are autonegotiating, and not done,
* return an error */
if
(
mii_info
->
autoneg
&&
!
(
status
&
BMSR_ANEGCOMPLETE
))
return
-
EAGAIN
;
return
0
;
return
0
;
}
}
/* Determine the speed and duplex which was negotiated */
static
int
genmii_read_status
(
struct
gfar_mii_info
*
mii_info
)
u16
mii_parse_88E1011_psr
(
u16
mii_reg
,
struct
net_device
*
dev
)
{
{
struct
gfar_private
*
priv
=
(
struct
gfar_private
*
)
dev
->
priv
;
u16
status
;
unsigned
int
speed
;
int
err
;
/* Update the link, but return if there
* was an error */
err
=
genmii_update_link
(
mii_info
);
if
(
err
)
return
err
;
if
(
priv
->
link
)
{
if
(
mii_info
->
autoneg
)
{
if
(
mii_reg
&
MIIM_88E1011_PHYSTAT_DUPLEX
)
status
=
phy_read
(
mii_info
,
MII_LPA
);
priv
->
duplexity
=
1
;
if
(
status
&
(
LPA_10FULL
|
LPA_100FULL
))
mii_info
->
duplex
=
DUPLEX_FULL
;
else
mii_info
->
duplex
=
DUPLEX_HALF
;
if
(
status
&
(
LPA_100FULL
|
LPA_100HALF
))
mii_info
->
speed
=
SPEED_100
;
else
else
priv
->
duplexity
=
0
;
mii_info
->
speed
=
SPEED_10
;
mii_info
->
pause
=
0
;
}
/* On non-aneg, we assume what we put in BMCR is the speed,
* though magic-aneg shouldn't prevent this case from occurring
*/
speed
=
(
mii_reg
&
MIIM_88E1011_PHYSTAT_SPEED
);
return
0
;
}
static
int
marvell_read_status
(
struct
gfar_mii_info
*
mii_info
)
{
u16
status
;
int
err
;
/* Update the link, but return if there
* was an error */
err
=
genmii_update_link
(
mii_info
);
if
(
err
)
return
err
;
/* If the link is up, read the speed and duplex */
/* If we aren't autonegotiating, assume speeds
* are as set */
if
(
mii_info
->
autoneg
&&
mii_info
->
link
)
{
int
speed
;
status
=
phy_read
(
mii_info
,
MII_M1011_PHY_SPEC_STATUS
);
#if 0
/* If speed and duplex aren't resolved,
* return an error. Isn't this handled
* by checking aneg?
*/
if ((status & MII_M1011_PHY_SPEC_STATUS_RESOLVED) == 0)
return -EAGAIN;
#endif
switch
(
speed
)
{
/* Get the duplexity */
case
MIIM_88E1011_PHYSTAT_GBIT
:
if
(
status
&
MII_M1011_PHY_SPEC_STATUS_FULLDUPLEX
)
priv
->
speed
=
1000
;
mii_info
->
duplex
=
DUPLEX_FULL
;
break
;
else
case
MIIM_88E1011_PHYSTAT_100
:
mii_info
->
duplex
=
DUPLEX_HALF
;
priv
->
speed
=
100
;
break
;
/* Get the speed */
default:
speed
=
status
&
MII_M1011_PHY_SPEC_STATUS_SPD_MASK
;
priv
->
speed
=
10
;
switch
(
speed
)
{
break
;
case
MII_M1011_PHY_SPEC_STATUS_1000
:
mii_info
->
speed
=
SPEED_1000
;
break
;
case
MII_M1011_PHY_SPEC_STATUS_100
:
mii_info
->
speed
=
SPEED_100
;
break
;
default:
mii_info
->
speed
=
SPEED_10
;
break
;
}
}
}
else
{
mii_info
->
pause
=
0
;
priv
->
speed
=
0
;
priv
->
duplexity
=
0
;
}
}
return
0
;
return
0
;
}
}
u16
mii_parse_cis8201
(
u16
mii_reg
,
struct
net_device
*
dev
)
{
struct
gfar_private
*
priv
=
(
struct
gfar_private
*
)
dev
->
priv
;
unsigned
int
speed
;
if
(
priv
->
link
)
{
static
int
cis820x_read_status
(
struct
gfar_mii_info
*
mii_info
)
if
(
mii_reg
&
MIIM_CIS8201_AUXCONSTAT_DUPLEX
)
{
priv
->
duplexity
=
1
;
u16
status
;
int
err
;
/* Update the link, but return if there
* was an error */
err
=
genmii_update_link
(
mii_info
);
if
(
err
)
return
err
;
/* If the link is up, read the speed and duplex */
/* If we aren't autonegotiating, assume speeds
* are as set */
if
(
mii_info
->
autoneg
&&
mii_info
->
link
)
{
int
speed
;
status
=
phy_read
(
mii_info
,
MII_CIS8201_AUX_CONSTAT
);
if
(
status
&
MII_CIS8201_AUXCONSTAT_DUPLEX
)
mii_info
->
duplex
=
DUPLEX_FULL
;
else
else
priv
->
duplexity
=
0
;
mii_info
->
duplex
=
DUPLEX_HALF
;
speed
=
mii_reg
&
MIIM
_CIS8201_AUXCONSTAT_SPEED
;
speed
=
status
&
MII
_CIS8201_AUXCONSTAT_SPEED
;
switch
(
speed
)
{
switch
(
speed
)
{
case
MII
M
_CIS8201_AUXCONSTAT_GBIT
:
case
MII_CIS8201_AUXCONSTAT_GBIT
:
priv
->
speed
=
1000
;
mii_info
->
speed
=
SPEED_
1000
;
break
;
break
;
case
MII
M
_CIS8201_AUXCONSTAT_100
:
case
MII_CIS8201_AUXCONSTAT_100
:
priv
->
speed
=
100
;
mii_info
->
speed
=
SPEED_
100
;
break
;
break
;
default:
default:
priv
->
speed
=
10
;
mii_info
->
speed
=
SPEED_
10
;
break
;
break
;
}
}
}
else
{
priv
->
speed
=
0
;
priv
->
duplexity
=
0
;
}
}
return
0
;
return
0
;
}
}
u16
mii_parse_dm9161_scsr
(
u16
mii_reg
,
struct
net_device
*
dev
)
static
int
marvell_ack_interrupt
(
struct
gfar_mii_info
*
mii_info
)
{
{
struct
gfar_private
*
priv
=
(
struct
gfar_private
*
)
dev
->
priv
;
/* Clear the interrupts by reading the reg */
phy_read
(
mii_info
,
MII_M1011_IEVENT
);
if
(
mii_reg
&
(
MIIM_DM9161_SCSR_100F
|
MIIM_DM9161_SCSR_100H
))
return
0
;
priv
->
speed
=
100
;
}
static
int
marvell_config_intr
(
struct
gfar_mii_info
*
mii_info
)
{
if
(
mii_info
->
interrupts
==
MII_INTERRUPT_ENABLED
)
phy_write
(
mii_info
,
MII_M1011_IMASK
,
MII_M1011_IMASK_INIT
);
else
else
priv
->
speed
=
10
;
phy_write
(
mii_info
,
MII_M1011_IMASK
,
MII_M1011_IMASK_CLEAR
);
return
0
;
}
static
int
cis820x_init
(
struct
gfar_mii_info
*
mii_info
)
{
phy_write
(
mii_info
,
MII_CIS8201_AUX_CONSTAT
,
MII_CIS8201_AUXCONSTAT_INIT
);
phy_write
(
mii_info
,
MII_CIS8201_EXT_CON1
,
MII_CIS8201_EXTCON1_INIT
);
if
(
mii_reg
&
(
MIIM_DM9161_SCSR_100F
|
MIIM_DM9161_SCSR_10F
))
return
0
;
priv
->
duplexity
=
1
;
}
static
int
cis820x_ack_interrupt
(
struct
gfar_mii_info
*
mii_info
)
{
phy_read
(
mii_info
,
MII_CIS8201_ISTAT
);
return
0
;
}
static
int
cis820x_config_intr
(
struct
gfar_mii_info
*
mii_info
)
{
if
(
mii_info
->
interrupts
==
MII_INTERRUPT_ENABLED
)
phy_write
(
mii_info
,
MII_CIS8201_IMASK
,
MII_CIS8201_IMASK_MASK
);
else
else
p
riv
->
duplexity
=
0
;
p
hy_write
(
mii_info
,
MII_CIS8201_IMASK
,
0
)
;
return
0
;
return
0
;
}
}
u16
dm9161_wait
(
u16
mii_reg
,
struct
net_device
*
dev
)
#define DM9161_DELAY 10
static
int
dm9161_read_status
(
struct
gfar_mii_info
*
mii_info
)
{
{
int
timeout
=
HZ
;
u16
status
;
int
secondary
=
10
;
int
err
;
u16
temp
;
/* Update the link, but return if there
* was an error */
err
=
genmii_update_link
(
mii_info
);
if
(
err
)
return
err
;
/* If the link is up, read the speed and duplex */
/* If we aren't autonegotiating, assume speeds
* are as set */
if
(
mii_info
->
autoneg
&&
mii_info
->
link
)
{
status
=
phy_read
(
mii_info
,
MII_DM9161_SCSR
);
if
(
status
&
(
MII_DM9161_SCSR_100F
|
MII_DM9161_SCSR_100H
))
mii_info
->
speed
=
SPEED_100
;
else
mii_info
->
speed
=
SPEED_10
;
do
{
if
(
status
&
(
MII_DM9161_SCSR_100F
|
MII_DM9161_SCSR_10F
))
mii_info
->
duplex
=
DUPLEX_FULL
;
else
mii_info
->
duplex
=
DUPLEX_HALF
;
}
return
0
;
}
/* Davicom takes a bit to come up after a reset,
* so wait here for a bit */
set_current_state
(
TASK_UNINTERRUPTIBLE
);
schedule_timeout
(
timeout
);
temp
=
read_phy_reg
(
dev
,
MIIM_STATUS
);
static
int
dm9161_config_aneg
(
struct
gfar_mii_info
*
mii_info
)
{
struct
dm9161_private
*
priv
=
mii_info
->
priv
;
secondary
--
;
if
(
0
==
priv
->
resetdone
)
}
while
((
!
(
temp
&
MIIM_STATUS_AN_DONE
))
&&
secondary
)
;
return
-
EAGAIN
;
return
0
;
return
0
;
}
}
static
struct
phy_info
phy_info_M88E1011S
=
{
static
void
dm9161_timer
(
unsigned
long
data
)
0x01410c6
,
{
"Marvell 88E1011S"
,
struct
gfar_mii_info
*
mii_info
=
(
struct
gfar_mii_info
*
)
data
;
4
,
struct
dm9161_private
*
priv
=
mii_info
->
priv
;
(
const
struct
phy_cmd
[])
{
/* config */
u16
status
=
phy_read
(
mii_info
,
MII_BMSR
);
/* Reset and configure the PHY */
{
MIIM_CONTROL
,
MIIM_CONTROL_INIT
,
mii_cr_init
},
if
(
status
&
BMSR_ANEGCOMPLETE
)
{
{
miim_end
,}
priv
->
resetdone
=
1
;
},
}
else
(
const
struct
phy_cmd
[])
{
/* startup */
mod_timer
(
&
priv
->
timer
,
jiffies
+
DM9161_DELAY
*
HZ
);
/* Status is read once to clear old link state */
}
{
MIIM_STATUS
,
miim_read
,
NULL
},
/* Auto-negotiate */
static
int
dm9161_init
(
struct
gfar_mii_info
*
mii_info
)
{
MIIM_STATUS
,
miim_read
,
mii_parse_sr
},
{
/* Read the status */
struct
dm9161_private
*
priv
;
{
MIIM_88E1011_PHY_STATUS
,
miim_read
,
mii_parse_88E1011_psr
},
/* Clear the IEVENT register */
/* Allocate the private data structure */
{
MIIM_88E1011_IEVENT
,
miim_read
,
NULL
},
priv
=
kmalloc
(
sizeof
(
struct
dm9161_private
),
GFP_KERNEL
);
/* Set up the mask */
{
MIIM_88E1011_IMASK
,
MIIM_88E1011_IMASK_INIT
,
NULL
},
if
(
NULL
==
priv
)
{
miim_end
,}
return
-
ENOMEM
;
},
(
const
struct
phy_cmd
[])
{
/* ack_int */
mii_info
->
priv
=
priv
;
/* Clear the interrupt */
{
MIIM_88E1011_IEVENT
,
miim_read
,
NULL
},
/* Reset is not done yet */
/* Disable interrupts */
priv
->
resetdone
=
0
;
{
MIIM_88E1011_IMASK
,
MIIM_88E1011_IMASK_CLEAR
,
NULL
},
{
miim_end
,}
/* Isolate the PHY */
},
phy_write
(
mii_info
,
MII_BMCR
,
BMCR_ISOLATE
);
(
const
struct
phy_cmd
[])
{
/* handle_int */
/* Read the Status (2x to make sure link is right) */
/* Do not bypass the scrambler/descrambler */
{
MIIM_STATUS
,
miim_read
,
NULL
},
phy_write
(
mii_info
,
MII_DM9161_SCR
,
MII_DM9161_SCR_INIT
);
/* Check the status */
{
MIIM_STATUS
,
miim_read
,
mii_parse_sr
},
/* Clear 10BTCSR to default */
{
MIIM_88E1011_PHY_STATUS
,
miim_read
,
mii_parse_88E1011_psr
},
phy_write
(
mii_info
,
MII_DM9161_10BTCSR
,
MII_DM9161_10BTCSR_INIT
);
/* Enable Interrupts */
{
MIIM_88E1011_IMASK
,
MIIM_88E1011_IMASK_INIT
,
NULL
},
/* Reconnect the PHY, and enable Autonegotiation */
{
miim_end
,}
phy_write
(
mii_info
,
MII_BMCR
,
BMCR_ANENABLE
);
},
(
const
struct
phy_cmd
[])
{
/* shutdown */
/* Start a timer for DM9161_DELAY seconds to wait
{
MIIM_88E1011_IEVENT
,
miim_read
,
NULL
},
* for the PHY to be ready */
{
MIIM_88E1011_IMASK
,
MIIM_88E1011_IMASK_CLEAR
,
NULL
},
init_timer
(
&
priv
->
timer
);
{
miim_end
,}
priv
->
timer
.
function
=
&
dm9161_timer
;
},
priv
->
timer
.
data
=
(
unsigned
long
)
mii_info
;
};
mod_timer
(
&
priv
->
timer
,
jiffies
+
DM9161_DELAY
*
HZ
);
return
0
;
}
static
void
dm9161_close
(
struct
gfar_mii_info
*
mii_info
)
{
struct
dm9161_private
*
priv
=
mii_info
->
priv
;
del_timer_sync
(
&
priv
->
timer
);
kfree
(
priv
);
}
#if 0
static int dm9161_ack_interrupt(struct gfar_mii_info *mii_info)
{
phy_read(mii_info, MII_DM9161_INTR);
return 0;
}
#endif
/* Cicada 820
4
*/
/* Cicada 820
x
*/
static
struct
phy_info
phy_info_cis820
4
=
{
static
struct
phy_info
phy_info_cis820
x
=
{
0x
3f11
,
0x
000fc440
,
"Cicada Cis8204"
,
"Cicada Cis8204"
,
6
,
0x000fffc0
,
(
const
struct
phy_cmd
[])
{
/* config */
.
features
=
MII_GBIT_FEATURES
,
/* Override PHY config settings */
.
init
=
&
cis820x_init
,
{
MIIM_CIS8201_AUX_CONSTAT
,
MIIM_CIS8201_AUXCONSTAT_INIT
,
NULL
},
.
config_aneg
=
&
gbit_config_aneg
,
/* Set up the interface mode */
.
read_status
=
&
cis820x_read_status
,
{
MIIM_CIS8201_EXT_CON1
,
MIIM_CIS8201_EXTCON1_INIT
,
NULL
},
.
ack_interrupt
=
&
cis820x_ack_interrupt
,
/* Configure some basic stuff */
.
config_intr
=
&
cis820x_config_intr
,
{
MIIM_CONTROL
,
MIIM_CONTROL_INIT
,
mii_cr_init
},
{
miim_end
,}
},
(
const
struct
phy_cmd
[])
{
/* startup */
/* Read the Status (2x to make sure link is right) */
{
MIIM_STATUS
,
miim_read
,
NULL
},
/* Auto-negotiate */
{
MIIM_STATUS
,
miim_read
,
mii_parse_sr
},
/* Read the status */
{
MIIM_CIS8201_AUX_CONSTAT
,
miim_read
,
mii_parse_cis8201
},
/* Clear the status register */
{
MIIM_CIS8204_ISTAT
,
miim_read
,
NULL
},
/* Enable interrupts */
{
MIIM_CIS8204_IMASK
,
MIIM_CIS8204_IMASK_MASK
,
NULL
},
{
miim_end
,}
},
(
const
struct
phy_cmd
[])
{
/* ack_int */
/* Clear the status register */
{
MIIM_CIS8204_ISTAT
,
miim_read
,
NULL
},
/* Disable interrupts */
{
MIIM_CIS8204_IMASK
,
0x0
,
NULL
},
{
miim_end
,}
},
(
const
struct
phy_cmd
[])
{
/* handle_int */
/* Read the Status (2x to make sure link is right) */
{
MIIM_STATUS
,
miim_read
,
NULL
},
/* Auto-negotiate */
{
MIIM_STATUS
,
miim_read
,
mii_parse_sr
},
/* Read the status */
{
MIIM_CIS8201_AUX_CONSTAT
,
miim_read
,
mii_parse_cis8201
},
/* Enable interrupts */
{
MIIM_CIS8204_IMASK
,
MIIM_CIS8204_IMASK_MASK
,
NULL
},
{
miim_end
,}
},
(
const
struct
phy_cmd
[])
{
/* shutdown */
/* Clear the status register */
{
MIIM_CIS8204_ISTAT
,
miim_read
,
NULL
},
/* Disable interrupts */
{
MIIM_CIS8204_IMASK
,
0x0
,
NULL
},
{
miim_end
,}
},
};
};
/* Cicada 8201 */
static
struct
phy_info
phy_info_dm9161
=
{
static
struct
phy_info
phy_info_cis8201
=
{
.
phy_id
=
0x0181b880
,
0xfc41
,
.
name
=
"Davicom DM9161E"
,
"CIS8201"
,
.
phy_id_mask
=
0x0ffffff0
,
4
,
.
init
=
dm9161_init
,
(
const
struct
phy_cmd
[])
{
/* config */
.
config_aneg
=
dm9161_config_aneg
,
/* Override PHY config settings */
.
read_status
=
dm9161_read_status
,
{
MIIM_CIS8201_AUX_CONSTAT
,
MIIM_CIS8201_AUXCONSTAT_INIT
,
NULL
},
.
close
=
dm9161_close
,
/* Set up the interface mode */
{
MIIM_CIS8201_EXT_CON1
,
MIIM_CIS8201_EXTCON1_INIT
,
NULL
},
/* Configure some basic stuff */
{
MIIM_CONTROL
,
MIIM_CONTROL_INIT
,
mii_cr_init
},
{
miim_end
,}
},
(
const
struct
phy_cmd
[])
{
/* startup */
/* Read the Status (2x to make sure link is right) */
{
MIIM_STATUS
,
miim_read
,
NULL
},
/* Auto-negotiate */
{
MIIM_STATUS
,
miim_read
,
mii_parse_sr
},
/* Read the status */
{
MIIM_CIS8201_AUX_CONSTAT
,
miim_read
,
mii_parse_cis8201
},
{
miim_end
,}
},
(
const
struct
phy_cmd
[])
{
/* ack_int */
{
miim_end
,}
},
(
const
struct
phy_cmd
[])
{
/* handle_int */
{
miim_end
,}
},
(
const
struct
phy_cmd
[])
{
/* shutdown */
{
miim_end
,}
},
};
};
static
struct
phy_info
phy_info_dm9161
=
{
static
struct
phy_info
phy_info_marvell
=
{
0x0181b88
,
.
phy_id
=
0x01410c00
,
"Davicom DM9161E"
,
.
phy_id_mask
=
0xffffff00
,
4
,
.
name
=
"Marvell 88E1101"
,
(
const
struct
phy_cmd
[])
{
/* config */
.
features
=
MII_GBIT_FEATURES
,
{
MIIM_CONTROL
,
MIIM_DM9161_CR_STOP
,
NULL
},
.
config_aneg
=
&
marvell_config_aneg
,
/* Do not bypass the scrambler/descrambler */
.
read_status
=
&
marvell_read_status
,
{
MIIM_DM9161_SCR
,
MIIM_DM9161_SCR_INIT
,
NULL
},
.
ack_interrupt
=
&
marvell_ack_interrupt
,
/* Clear 10BTCSR to default */
.
config_intr
=
&
marvell_config_intr
,
{
MIIM_DM9161_10BTCSR
,
MIIM_DM9161_10BTCSR_INIT
,
NULL
},
};
/* Configure some basic stuff */
{
MIIM_CONTROL
,
MIIM_CR_INIT
,
NULL
},
static
struct
phy_info
phy_info_genmii
=
{
{
miim_end
,}
.
phy_id
=
0x00000000
,
},
.
phy_id_mask
=
0x00000000
,
(
const
struct
phy_cmd
[])
{
/* startup */
.
name
=
"Generic MII"
,
/* Restart Auto Negotiation */
.
features
=
MII_BASIC_FEATURES
,
{
MIIM_CONTROL
,
MIIM_DM9161_CR_RSTAN
,
NULL
},
.
config_aneg
=
genmii_config_aneg
,
/* Status is read once to clear old link state */
.
read_status
=
genmii_read_status
,
{
MIIM_STATUS
,
miim_read
,
dm9161_wait
},
/* Auto-negotiate */
{
MIIM_STATUS
,
miim_read
,
mii_parse_sr
},
/* Read the status */
{
MIIM_DM9161_SCSR
,
miim_read
,
mii_parse_dm9161_scsr
},
/* Clear any pending interrupts */
{
MIIM_DM9161_INTR
,
miim_read
,
NULL
},
{
miim_end
,}
},
(
const
struct
phy_cmd
[])
{
/* ack_int */
{
MIIM_DM9161_INTR
,
miim_read
,
NULL
},
{
miim_end
,}
},
(
const
struct
phy_cmd
[])
{
/* handle_int */
{
MIIM_STATUS
,
miim_read
,
NULL
},
{
MIIM_STATUS
,
miim_read
,
mii_parse_sr
},
{
MIIM_DM9161_SCSR
,
miim_read
,
mii_parse_dm9161_scsr
},
{
miim_end
,}
},
(
const
struct
phy_cmd
[])
{
/* shutdown */
{
MIIM_DM9161_INTR
,
miim_read
,
NULL
},
{
miim_end
,}
},
};
};
static
struct
phy_info
*
phy_info
[]
=
{
static
struct
phy_info
*
phy_info
[]
=
{
&
phy_info_cis8201
,
&
phy_info_cis820x
,
&
phy_info_cis8204
,
&
phy_info_marvell
,
&
phy_info_M88E1011S
,
&
phy_info_dm9161
,
&
phy_info_dm9161
,
&
phy_info_genmii
,
NULL
NULL
};
};
u16
phy_read
(
struct
gfar_mii_info
*
mii_info
,
u16
regnum
)
{
u16
retval
;
unsigned
long
flags
;
spin_lock_irqsave
(
&
mii_info
->
mdio_lock
,
flags
);
retval
=
mii_info
->
mdio_read
(
mii_info
->
dev
,
mii_info
->
mii_id
,
regnum
);
spin_unlock_irqrestore
(
&
mii_info
->
mdio_lock
,
flags
);
return
retval
;
}
void
phy_write
(
struct
gfar_mii_info
*
mii_info
,
u16
regnum
,
u16
val
)
{
unsigned
long
flags
;
spin_lock_irqsave
(
&
mii_info
->
mdio_lock
,
flags
);
mii_info
->
mdio_write
(
mii_info
->
dev
,
mii_info
->
mii_id
,
regnum
,
val
);
spin_unlock_irqrestore
(
&
mii_info
->
mdio_lock
,
flags
);
}
/* Use the PHY ID registers to determine what type of PHY is attached
/* Use the PHY ID registers to determine what type of PHY is attached
* to device dev. return a struct phy_info structure describing that PHY
* to device dev. return a struct phy_info structure describing that PHY
*/
*/
struct
phy_info
*
get_phy_info
(
struct
net_device
*
dev
)
struct
phy_info
*
get_phy_info
(
struct
gfar_mii_info
*
mii_info
)
{
{
u16
phy_reg
;
u16
phy_reg
;
u32
phy_ID
;
u32
phy_ID
;
int
i
;
int
i
;
struct
phy_info
*
theInfo
=
NULL
;
struct
phy_info
*
theInfo
=
NULL
;
struct
net_device
*
dev
=
mii_info
->
dev
;
/* Grab the bits from PHYIR1, and put them in the upper half */
/* Grab the bits from PHYIR1, and put them in the upper half */
phy_reg
=
read_phy_reg
(
dev
,
MIIM_PHYIR
1
);
phy_reg
=
phy_read
(
mii_info
,
MII_PHYSID
1
);
phy_ID
=
(
phy_reg
&
0xffff
)
<<
16
;
phy_ID
=
(
phy_reg
&
0xffff
)
<<
16
;
/* Grab the bits from PHYIR2, and put them in the lower half */
/* Grab the bits from PHYIR2, and put them in the lower half */
phy_reg
=
read_phy_reg
(
dev
,
MIIM_PHYIR
2
);
phy_reg
=
phy_read
(
mii_info
,
MII_PHYSID
2
);
phy_ID
|=
(
phy_reg
&
0xffff
);
phy_ID
|=
(
phy_reg
&
0xffff
);
/* loop through all the known PHY types, and find one that */
/* loop through all the known PHY types, and find one that */
/* matches the ID we read from the PHY. */
/* matches the ID we read from the PHY. */
for
(
i
=
0
;
phy_info
[
i
];
i
++
)
for
(
i
=
0
;
phy_info
[
i
];
i
++
)
if
(
phy_info
[
i
]
->
id
==
(
phy_ID
>>
phy_info
[
i
]
->
shift
))
if
(
phy_info
[
i
]
->
phy_id
==
(
phy_ID
&
phy_info
[
i
]
->
phy_id_mask
))
{
theInfo
=
phy_info
[
i
];
theInfo
=
phy_info
[
i
];
break
;
}
/* This shouldn't happen, as we have generic PHY support */
if
(
theInfo
==
NULL
)
{
if
(
theInfo
==
NULL
)
{
printk
(
"%s: PHY id %x is not supported!
\n
"
,
dev
->
name
,
phy_ID
);
printk
(
"%s: PHY id %x is not supported!
\n
"
,
dev
->
name
,
phy_ID
);
return
NULL
;
return
NULL
;
...
@@ -455,50 +659,3 @@ struct phy_info * get_phy_info(struct net_device *dev)
...
@@ -455,50 +659,3 @@ struct phy_info * get_phy_info(struct net_device *dev)
return
theInfo
;
return
theInfo
;
}
}
/* Take a list of struct phy_cmd, and, depending on the values, either */
/* read or write, using a helper function if provided */
/* It is assumed that all lists of struct phy_cmd will be terminated by */
/* mii_end. */
void
phy_run_commands
(
struct
net_device
*
dev
,
const
struct
phy_cmd
*
cmd
)
{
int
i
;
u16
result
;
struct
gfar_private
*
priv
=
(
struct
gfar_private
*
)
dev
->
priv
;
struct
gfar
*
phyregs
=
priv
->
phyregs
;
/* Reset the management interface */
gfar_write
(
&
phyregs
->
miimcfg
,
MIIMCFG_RESET
);
/* Setup the MII Mgmt clock speed */
gfar_write
(
&
phyregs
->
miimcfg
,
MIIMCFG_INIT_VALUE
);
/* Wait until the bus is free */
while
(
gfar_read
(
&
phyregs
->
miimind
)
&
MIIMIND_BUSY
)
cpu_relax
();
for
(
i
=
0
;
cmd
->
mii_reg
!=
miim_end
;
i
++
)
{
/* The command is a read if mii_data is miim_read */
if
(
cmd
->
mii_data
==
miim_read
)
{
/* Read the value of the PHY reg */
result
=
read_phy_reg
(
dev
,
cmd
->
mii_reg
);
/* If a function was supplied, we need to let it process */
/* the result. */
if
(
cmd
->
funct
!=
NULL
)
(
*
(
cmd
->
funct
))
(
result
,
dev
);
}
else
{
/* Otherwise, it's a write */
/* If a function was supplied, it will provide
* the value to write */
/* Otherwise, the value was supplied in cmd->mii_data */
if
(
cmd
->
funct
!=
NULL
)
result
=
(
*
(
cmd
->
funct
))
(
0
,
dev
);
else
result
=
cmd
->
mii_data
;
/* Write the appropriate value to the PHY reg */
write_phy_reg
(
dev
,
cmd
->
mii_reg
,
result
);
}
cmd
++
;
}
}
drivers/net/gianfar_phy.h
View file @
f3c1e4cf
...
@@ -8,7 +8,7 @@
...
@@ -8,7 +8,7 @@
* Author: Andy Fleming
* Author: Andy Fleming
* Maintainer: Kumar Gala (kumar.gala@freescale.com)
* Maintainer: Kumar Gala (kumar.gala@freescale.com)
*
*
* Copyright
2004 Freescale Semiconductor, Inc
* Copyright
(c) 2002-2004 Freescale Semiconductor, Inc.
*
*
* This program is free software; you can redistribute it and/or modify it
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* under the terms of the GNU General Public License as published by the
...
@@ -19,135 +19,144 @@
...
@@ -19,135 +19,144 @@
#ifndef __GIANFAR_PHY_H
#ifndef __GIANFAR_PHY_H
#define __GIANFAR_PHY_H
#define __GIANFAR_PHY_H
#define
miim
_end ((u32)-2)
#define
MII
_end ((u32)-2)
#define
miim
_read ((u32)-1)
#define
MII
_read ((u32)-1)
#define MIIMIND_BUSY 0x00000001
#define MIIMIND_BUSY 0x00000001
#define MIIMIND_NOTVALID 0x00000004
#define MIIMIND_NOTVALID 0x00000004
#define MIIM_CONTROL 0x00
#define GFAR_AN_TIMEOUT 2000
#define MIIM_CONTROL_RESET 0x00008000
#define MIIM_CONTROL_INIT 0x00001140
#define MIIM_ANEN 0x00001000
#define MIIM_CR 0x00
#define MIIM_CR_RST 0x00008000
#define MIIM_CR_INIT 0x00001000
#define MIIM_STATUS 0x1
#define MIIM_STATUS_AN_DONE 0x00000020
#define MIIM_STATUS_LINK 0x0004
#define MIIM_PHYIR1 0x2
#define MIIM_PHYIR2 0x3
#define GFAR_AN_TIMEOUT 0x000fffff
#define MIIM_ANLPBPA 0x5
#define MIIM_ANLPBPA_HALF 0x00000040
#define MIIM_ANLPBPA_FULL 0x00000020
#define MIIM_ANEX 0x6
#define MIIM_ANEX_NP 0x00000004
#define MIIM_ANEX_PRX 0x00000002
/* 1000BT control (Marvell & BCM54xx at least) */
#define MII_1000BASETCONTROL 0x09
#define MII_1000BASETCONTROL_FULLDUPLEXCAP 0x0200
#define MII_1000BASETCONTROL_HALFDUPLEXCAP 0x0100
/* Cicada Extended Control Register 1 */
/* Cicada Extended Control Register 1 */
#define MII
M
_CIS8201_EXT_CON1 0x17
#define MII_CIS8201_EXT_CON1 0x17
#define MII
M
_CIS8201_EXTCON1_INIT 0x0000
#define MII_CIS8201_EXTCON1_INIT 0x0000
/* Cicada Interrupt Mask Register */
/* Cicada Interrupt Mask Register */
#define MII
M_CIS8204
_IMASK 0x19
#define MII
_CIS8201
_IMASK 0x19
#define MII
M_CIS8204
_IMASK_IEN 0x8000
#define MII
_CIS8201
_IMASK_IEN 0x8000
#define MII
M_CIS8204
_IMASK_SPEED 0x4000
#define MII
_CIS8201
_IMASK_SPEED 0x4000
#define MII
M_CIS8204
_IMASK_LINK 0x2000
#define MII
_CIS8201
_IMASK_LINK 0x2000
#define MII
M_CIS8204
_IMASK_DUPLEX 0x1000
#define MII
_CIS8201
_IMASK_DUPLEX 0x1000
#define MII
M_CIS8204
_IMASK_MASK 0xf000
#define MII
_CIS8201
_IMASK_MASK 0xf000
/* Cicada Interrupt Status Register */
/* Cicada Interrupt Status Register */
#define MII
M_CIS8204
_ISTAT 0x1a
#define MII
_CIS8201
_ISTAT 0x1a
#define MII
M_CIS8204
_ISTAT_STATUS 0x8000
#define MII
_CIS8201
_ISTAT_STATUS 0x8000
#define MII
M_CIS8204
_ISTAT_SPEED 0x4000
#define MII
_CIS8201
_ISTAT_SPEED 0x4000
#define MII
M_CIS8204
_ISTAT_LINK 0x2000
#define MII
_CIS8201
_ISTAT_LINK 0x2000
#define MII
M_CIS8204
_ISTAT_DUPLEX 0x1000
#define MII
_CIS8201
_ISTAT_DUPLEX 0x1000
/* Cicada Auxiliary Control/Status Register */
/* Cicada Auxiliary Control/Status Register */
#define MII
M
_CIS8201_AUX_CONSTAT 0x1c
#define MII_CIS8201_AUX_CONSTAT 0x1c
#define MII
M
_CIS8201_AUXCONSTAT_INIT 0x0004
#define MII_CIS8201_AUXCONSTAT_INIT 0x0004
#define MII
M
_CIS8201_AUXCONSTAT_DUPLEX 0x0020
#define MII_CIS8201_AUXCONSTAT_DUPLEX 0x0020
#define MII
M
_CIS8201_AUXCONSTAT_SPEED 0x0018
#define MII_CIS8201_AUXCONSTAT_SPEED 0x0018
#define MII
M
_CIS8201_AUXCONSTAT_GBIT 0x0010
#define MII_CIS8201_AUXCONSTAT_GBIT 0x0010
#define MII
M
_CIS8201_AUXCONSTAT_100 0x0008
#define MII_CIS8201_AUXCONSTAT_100 0x0008
/* 88E1011 PHY Status Register */
/* 88E1011 PHY Status Register */
#define MIIM_88E1011_PHY_STATUS 0x11
#define MII_M1011_PHY_SPEC_STATUS 0x11
#define MIIM_88E1011_PHYSTAT_SPEED 0xc000
#define MII_M1011_PHY_SPEC_STATUS_1000 0x8000
#define MIIM_88E1011_PHYSTAT_GBIT 0x8000
#define MII_M1011_PHY_SPEC_STATUS_100 0x4000
#define MIIM_88E1011_PHYSTAT_100 0x4000
#define MII_M1011_PHY_SPEC_STATUS_SPD_MASK 0xc000
#define MIIM_88E1011_PHYSTAT_DUPLEX 0x2000
#define MII_M1011_PHY_SPEC_STATUS_FULLDUPLEX 0x2000
#define MIIM_88E1011_PHYSTAT_LINK 0x0400
#define MII_M1011_PHY_SPEC_STATUS_RESOLVED 0x0800
#define MII_M1011_PHY_SPEC_STATUS_LINK 0x0400
#define MII
M_88E
1011_IEVENT 0x13
#define MII
_M
1011_IEVENT 0x13
#define MII
M_88E1011_IEVENT_CLEAR
0x0000
#define MII
_M1011_IEVENT_CLEAR
0x0000
#define MII
M_88E1011_IMASK
0x12
#define MII
_M1011_IMASK
0x12
#define MII
M_88E
1011_IMASK_INIT 0x6400
#define MII
_M
1011_IMASK_INIT 0x6400
#define MII
M_88E1011_IMASK_CLEAR
0x0000
#define MII
_M1011_IMASK_CLEAR
0x0000
/* DM9161 Control register values */
#define MII_DM9161_SCR 0x10
#define MIIM_DM9161_CR_STOP 0x0400
#define MII_DM9161_SCR_INIT 0x0610
#define MIIM_DM9161_CR_RSTAN 0x1200
#define MIIM_DM9161_SCR 0x10
#define MIIM_DM9161_SCR_INIT 0x0610
/* DM9161 Specified Configuration and Status Register */
/* DM9161 Specified Configuration and Status Register */
#define MII
M
_DM9161_SCSR 0x11
#define MII_DM9161_SCSR 0x11
#define MII
M
_DM9161_SCSR_100F 0x8000
#define MII_DM9161_SCSR_100F 0x8000
#define MII
M
_DM9161_SCSR_100H 0x4000
#define MII_DM9161_SCSR_100H 0x4000
#define MII
M
_DM9161_SCSR_10F 0x2000
#define MII_DM9161_SCSR_10F 0x2000
#define MII
M
_DM9161_SCSR_10H 0x1000
#define MII_DM9161_SCSR_10H 0x1000
/* DM9161 Interrupt Register */
/* DM9161 Interrupt Register */
#define MII
M
_DM9161_INTR 0x15
#define MII_DM9161_INTR 0x15
#define MII
M
_DM9161_INTR_PEND 0x8000
#define MII_DM9161_INTR_PEND 0x8000
#define MII
M
_DM9161_INTR_DPLX_MASK 0x0800
#define MII_DM9161_INTR_DPLX_MASK 0x0800
#define MII
M
_DM9161_INTR_SPD_MASK 0x0400
#define MII_DM9161_INTR_SPD_MASK 0x0400
#define MII
M
_DM9161_INTR_LINK_MASK 0x0200
#define MII_DM9161_INTR_LINK_MASK 0x0200
#define MII
M
_DM9161_INTR_MASK 0x0100
#define MII_DM9161_INTR_MASK 0x0100
#define MII
M
_DM9161_INTR_DPLX_CHANGE 0x0010
#define MII_DM9161_INTR_DPLX_CHANGE 0x0010
#define MII
M
_DM9161_INTR_SPD_CHANGE 0x0008
#define MII_DM9161_INTR_SPD_CHANGE 0x0008
#define MII
M
_DM9161_INTR_LINK_CHANGE 0x0004
#define MII_DM9161_INTR_LINK_CHANGE 0x0004
#define MII
M
_DM9161_INTR_INIT 0x0000
#define MII_DM9161_INTR_INIT 0x0000
#define MII
M
_DM9161_INTR_STOP \
#define MII_DM9161_INTR_STOP \
(MII
M_DM9161_INTR_DPLX_MASK | MIIM
_DM9161_INTR_SPD_MASK \
(MII
_DM9161_INTR_DPLX_MASK | MII
_DM9161_INTR_SPD_MASK \
| MII
M_DM9161_INTR_LINK_MASK | MIIM
_DM9161_INTR_MASK)
| MII
_DM9161_INTR_LINK_MASK | MII
_DM9161_INTR_MASK)
/* DM9161 10BT Configuration/Status */
/* DM9161 10BT Configuration/Status */
#define MIIM_DM9161_10BTCSR 0x12
#define MII_DM9161_10BTCSR 0x12
#define MIIM_DM9161_10BTCSR_INIT 0x7800
#define MII_DM9161_10BTCSR_INIT 0x7800
#define MII_BASIC_FEATURES (SUPPORTED_10baseT_Half | \
#define MIIM_READ_COMMAND 0x00000001
SUPPORTED_10baseT_Full | \
SUPPORTED_100baseT_Half | \
/*
SUPPORTED_100baseT_Full | \
* struct phy_cmd: A command for reading or writing a PHY register
SUPPORTED_Autoneg | \
*
SUPPORTED_TP | \
* mii_reg: The register to read or write
SUPPORTED_MII)
*
* mii_data: For writes, the value to put in the register.
#define MII_GBIT_FEATURES (MII_BASIC_FEATURES | \
* A value of -1 indicates this is a read.
SUPPORTED_1000baseT_Half | \
*
SUPPORTED_1000baseT_Full)
* funct: A function pointer which is invoked for each command.
* For reads, this function will be passed the value read
#define MII_READ_COMMAND 0x00000001
* from the PHY, and process it.
* For writes, the result of this function will be written
#define MII_INTERRUPT_DISABLED 0x0
* to the PHY register
#define MII_INTERRUPT_ENABLED 0x1
*/
/* Taken from mii_if_info and sungem_phy.h */
struct
phy_cmd
{
struct
gfar_mii_info
{
u32
mii_reg
;
/* Information about the PHY type */
u32
mii_data
;
/* And management functions */
u16
(
*
funct
)
(
u16
mii_reg
,
struct
net_device
*
dev
);
struct
phy_info
*
phyinfo
;
/* forced speed & duplex (no autoneg)
* partner speed & duplex & pause (autoneg)
*/
int
speed
;
int
duplex
;
int
pause
;
/* The most recently read link state */
int
link
;
/* Enabled Interrupts */
u32
interrupts
;
u32
advertising
;
int
autoneg
;
int
mii_id
;
/* private data pointer */
/* For use by PHYs to maintain extra state */
void
*
priv
;
/* Provided by host chip */
struct
net_device
*
dev
;
/* A lock to ensure that only one thing can read/write
* the MDIO bus at a time */
spinlock_t
mdio_lock
;
/* Provided by ethernet driver */
int
(
*
mdio_read
)
(
struct
net_device
*
dev
,
int
mii_id
,
int
reg
);
void
(
*
mdio_write
)
(
struct
net_device
*
dev
,
int
mii_id
,
int
reg
,
int
val
);
};
};
/* struct phy_info: a structure which defines attributes for a PHY
/* struct phy_info: a structure which defines attributes for a PHY
...
@@ -155,38 +164,50 @@ struct phy_cmd {
...
@@ -155,38 +164,50 @@ struct phy_cmd {
* id will contain a number which represents the PHY. During
* id will contain a number which represents the PHY. During
* startup, the driver will poll the PHY to find out what its
* startup, the driver will poll the PHY to find out what its
* UID--as defined by registers 2 and 3--is. The 32-bit result
* UID--as defined by registers 2 and 3--is. The 32-bit result
* gotten from the PHY will be
shifted right by "shift" bits
to
* gotten from the PHY will be
ANDed with phy_id_mask
to
* discard any bits which may change based on revision numbers
* discard any bits which may change based on revision numbers
* unimportant to functionality
* unimportant to functionality
*
*
* The
struct phy_cmd entries represent pointers to an arrays of
* The
re are 6 commands which take a gfar_mii_info structure.
*
commands which tell the driver what to do to the PHY
.
*
Each PHY must declare config_aneg, and read_status
.
*/
*/
struct
phy_info
{
struct
phy_info
{
u32
id
;
u32
phy_id
;
char
*
name
;
char
*
name
;
unsigned
int
shift
;
unsigned
int
phy_id_mask
;
/* Called to configure the PHY, and modify the controller
u32
features
;
* based on the results */
const
struct
phy_cmd
*
config
;
/* Called to initialize the PHY */
int
(
*
init
)(
struct
gfar_mii_info
*
mii_info
);
/* Called when starting up the controller. Usually sets
* up the interrupt for state changes */
/* Called to suspend the PHY for power */
const
struct
phy_cmd
*
startup
;
int
(
*
suspend
)(
struct
gfar_mii_info
*
mii_info
);
/* Called inside the interrupt handler to acknowledge
/* Reconfigures autonegotiation (or disables it) */
* the interrupt */
int
(
*
config_aneg
)(
struct
gfar_mii_info
*
mii_info
);
const
struct
phy_cmd
*
ack_int
;
/* Determines the negotiated speed and duplex */
/* Called in the bottom half to handle the interrupt */
int
(
*
read_status
)(
struct
gfar_mii_info
*
mii_info
);
const
struct
phy_cmd
*
handle_int
;
/* Clears any pending interrupts */
/* Called when bringing down the controller. Usually stops
int
(
*
ack_interrupt
)(
struct
gfar_mii_info
*
mii_info
);
* the interrupts from being generated */
const
struct
phy_cmd
*
shutdown
;
/* Enables or disables interrupts */
int
(
*
config_intr
)(
struct
gfar_mii_info
*
mii_info
);
/* Clears up any memory if needed */
void
(
*
close
)(
struct
gfar_mii_info
*
mii_info
);
};
};
struct
phy_info
*
get_phy_info
(
struct
net_device
*
dev
);
struct
phy_info
*
get_phy_info
(
struct
gfar_mii_info
*
mii_info
);
void
phy_run_commands
(
struct
net_device
*
dev
,
const
struct
phy_cmd
*
cmd
);
int
read_phy_reg
(
struct
net_device
*
dev
,
int
mii_id
,
int
regnum
);
void
write_phy_reg
(
struct
net_device
*
dev
,
int
mii_id
,
int
regnum
,
int
value
);
void
mii_clear_phy_interrupt
(
struct
gfar_mii_info
*
mii_info
);
void
mii_configure_phy_interrupt
(
struct
gfar_mii_info
*
mii_info
,
u32
interrupts
);
struct
dm9161_private
{
struct
timer_list
timer
;
int
resetdone
;
};
#endif
/* GIANFAR_PHY_H */
#endif
/* GIANFAR_PHY_H */
include/linux/mii.h
View file @
f3c1e4cf
...
@@ -33,7 +33,8 @@
...
@@ -33,7 +33,8 @@
#define MII_NCONFIG 0x1c
/* Network interface config */
#define MII_NCONFIG 0x1c
/* Network interface config */
/* Basic mode control register. */
/* Basic mode control register. */
#define BMCR_RESV 0x007f
/* Unused... */
#define BMCR_RESV 0x003f
/* Unused... */
#define BMCR_SPEED1000 0x0040
/* MSB of Speed (1000) */
#define BMCR_CTST 0x0080
/* Collision test */
#define BMCR_CTST 0x0080
/* Collision test */
#define BMCR_FULLDPLX 0x0100
/* Full duplex */
#define BMCR_FULLDPLX 0x0100
/* Full duplex */
#define BMCR_ANRESTART 0x0200
/* Auto negotiation restart */
#define BMCR_ANRESTART 0x0200
/* Auto negotiation restart */
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment