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
Kirill Smelkov
linux
Commits
aa6af1e1
Commit
aa6af1e1
authored
Jun 10, 2004
by
Russell King
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
[PATCH] add ARM smc91x driver
parent
d66f55e6
Changes
4
Hide whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
3018 additions
and
0 deletions
+3018
-0
drivers/net/arm/Kconfig
drivers/net/arm/Kconfig
+17
-0
drivers/net/arm/Makefile
drivers/net/arm/Makefile
+1
-0
drivers/net/arm/smc91x.c
drivers/net/arm/smc91x.c
+2171
-0
drivers/net/arm/smc91x.h
drivers/net/arm/smc91x.h
+829
-0
No files found.
drivers/net/arm/Kconfig
View file @
aa6af1e1
...
...
@@ -45,3 +45,20 @@ config ARM_ETHER00
number (MTD support is required for this). Otherwise you will
need to set a suitable hw address using ifconfig.
config SMC91X
tristate "SMC 91C9x/91C1xxx support"
select CRC32
select MII
depends on ARM
help
This is a driver for SMC's 91x series of Ethernet chipsets,
including the SMC91C94 and the SMC91C111. Say Y if you want it
compiled into the kernel, and read the file
<file:Documentation/networking/smc9.txt> and the Ethernet-HOWTO,
available from <http://www.linuxdoc.org/docs.html#howto>.
This driver is also available as a module ( = code which can be
inserted in and removed from the running kernel whenever you want).
The module will be called smc91x. If you want to compile it as a
module, say M here and read <file:Documentation/modules.txt> as well
as <file:Documentation/networking/net-modules.txt>.
drivers/net/arm/Makefile
View file @
aa6af1e1
...
...
@@ -8,3 +8,4 @@ obj-$(CONFIG_ARM_ETHER00) += ether00.o
obj-$(CONFIG_ARM_ETHERH)
+=
etherh.o
obj-$(CONFIG_ARM_ETHER3)
+=
ether3.o
obj-$(CONFIG_ARM_ETHER1)
+=
ether1.o
obj-$(CONFIG_SMC91X)
+=
smc91x.o
drivers/net/arm/smc91x.c
0 → 100644
View file @
aa6af1e1
/*
* smc91x.c
* This is a driver for SMSC's 91C9x/91C1xx single-chip Ethernet devices.
*
* Copyright (C) 1996 by Erik Stahlman
* Copyright (C) 2001 Standard Microsystems Corporation
* Developed by Simple Network Magic Corporation
* Copyright (C) 2003 Monta Vista Software, Inc.
* Unified SMC91x driver by Nicolas Pitre
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* Arguments:
* io = for the base address
* irq = for the IRQ
* nowait = 0 for normal wait states, 1 eliminates additional wait states
*
* original author:
* Erik Stahlman <erik@vt.edu>
*
* hardware multicast code:
* Peter Cammaert <pc@denkart.be>
*
* contributors:
* Daris A Nevil <dnevil@snmc.com>
* Nicolas Pitre <nico@cam.org>
* Russell King <rmk@arm.linux.org.uk>
*
* History:
* 08/20/00 Arnaldo Melo fix kfree(skb) in smc_hardware_send_packet
* 12/15/00 Christian Jullien fix "Warning: kfree_skb on hard IRQ"
* 03/16/01 Daris A Nevil modified smc9194.c for use with LAN91C111
* 08/22/01 Scott Anderson merge changes from smc9194 to smc91111
* 08/21/01 Pramod B Bhardwaj added support for RevB of LAN91C111
* 12/20/01 Jeff Sutherland initial port to Xscale PXA with DMA support
* 04/07/03 Nicolas Pitre unified SMC91x driver, killed irq races,
* more bus abstraction, big cleanup, etc.
* 29/09/03 Russell King - add driver model support
* - ethtool support
* - convert to use generic MII interface
* - add link up/down notification
* - don't try to handle full negotiation in
* smc_phy_configure
* - clean up (and fix stack overrun) in PHY
* MII read/write functions
*/
static
const
char
version
[]
=
"smc91x.c: v1.0, mar 07 2003 by Nicolas Pitre <nico@cam.org>
\n
"
;
/* Debugging level */
#ifndef SMC_DEBUG
#define SMC_DEBUG 0
#endif
#include <linux/config.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/delay.h>
#include <linux/timer.h>
#include <linux/errno.h>
#include <linux/ioport.h>
#include <linux/crc32.h>
#include <linux/device.h>
#include <linux/spinlock.h>
#include <linux/ethtool.h>
#include <linux/mii.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/skbuff.h>
#include <asm/io.h>
#include <asm/hardware.h>
#include <asm/irq.h>
#include "smc91x.h"
#ifdef CONFIG_ISA
/*
* the LAN91C111 can be at any of the following port addresses. To change,
* for a slightly different card, you can add it to the array. Keep in
* mind that the array must end in zero.
*/
static
unsigned
int
smc_portlist
[]
__initdata
=
{
0x200
,
0x220
,
0x240
,
0x260
,
0x280
,
0x2A0
,
0x2C0
,
0x2E0
,
0x300
,
0x320
,
0x340
,
0x360
,
0x380
,
0x3A0
,
0x3C0
,
0x3E0
,
0
};
#ifndef SMC_IOADDR
# define SMC_IOADDR -1
#endif
static
unsigned
long
io
=
SMC_IOADDR
;
module_param
(
io
,
ulong
,
0400
);
MODULE_PARM_DESC
(
io
,
"I/O base address"
);
#ifndef SMC_IRQ
# define SMC_IRQ -1
#endif
static
int
irq
=
SMC_IRQ
;
module_param
(
irq
,
int
,
0400
);
MODULE_PARM_DESC
(
irq
,
"IRQ number"
);
#endif
/* CONFIG_ISA */
#ifndef SMC_NOWAIT
# define SMC_NOWAIT 0
#endif
static
int
nowait
=
SMC_NOWAIT
;
module_param
(
nowait
,
int
,
0400
);
MODULE_PARM_DESC
(
nowait
,
"set to 1 for no wait state"
);
/*
* Transmit timeout, default 5 seconds.
*/
static
int
watchdog
=
5000
;
module_param
(
watchdog
,
int
,
0400
);
MODULE_PARM_DESC
(
watchdog
,
"transmit timeout in milliseconds"
);
MODULE_LICENSE
(
"GPL"
);
/*
* The internal workings of the driver. If you are changing anything
* here with the SMC stuff, you should have the datasheet and know
* what you are doing.
*/
#define CARDNAME "smc91x"
/*
* Use power-down feature of the chip
*/
#define POWER_DOWN 1
/*
* Wait time for memory to be free. This probably shouldn't be
* tuned that much, as waiting for this means nothing else happens
* in the system
*/
#define MEMORY_WAIT_TIME 16
/*
* This selects whether TX packets are sent one by one to the SMC91x internal
* memory and throttled until transmission completes. This may prevent
* RX overruns a litle by keeping much of the memory free for RX packets
* but to the expense of reduced TX throughput and increased IRQ overhead.
* Note this is not a cure for a too slow data bus or too high IRQ latency.
*/
#define THROTTLE_TX_PKTS 0
/*
* The MII clock high/low times. 2x this number gives the MII clock period
* in microseconds. (was 50, but this gives 6.4ms for each MII transaction!)
*/
#define MII_DELAY 1
/* store this information for the driver.. */
struct
smc_local
{
/*
* If I have to wait until memory is available to send a
* packet, I will store the skbuff here, until I get the
* desired memory. Then, I'll send it out and free it.
*/
struct
sk_buff
*
saved_skb
;
/*
* these are things that the kernel wants me to keep, so users
* can find out semi-useless statistics of how well the card is
* performing
*/
struct
net_device_stats
stats
;
/* version/revision of the SMC91x chip */
int
version
;
/* Contains the current active transmission mode */
int
tcr_cur_mode
;
/* Contains the current active receive mode */
int
rcr_cur_mode
;
/* Contains the current active receive/phy mode */
int
rpc_cur_mode
;
int
ctl_rfduplx
;
int
ctl_rspeed
;
u32
msg_enable
;
u32
phy_type
;
struct
mii_if_info
mii
;
spinlock_t
lock
;
#ifdef SMC_USE_PXA_DMA
/* DMA needs the physical address of the chip */
u_long
physaddr
;
#endif
};
#if SMC_DEBUG > 0
#define DBG(n, args...) \
do { \
if (SMC_DEBUG >= (n)) \
printk(KERN_DEBUG args); \
} while (0)
#define PRINTK(args...) printk(args)
#else
#define DBG(n, args...) do { } while(0)
#define PRINTK(args...) printk(KERN_DEBUG args)
#endif
#if SMC_DEBUG > 3
static
void
PRINT_PKT
(
u_char
*
buf
,
int
length
)
{
int
i
;
int
remainder
;
int
lines
;
lines
=
length
/
16
;
remainder
=
length
%
16
;
for
(
i
=
0
;
i
<
lines
;
i
++
)
{
int
cur
;
for
(
cur
=
0
;
cur
<
8
;
cur
++
)
{
u_char
a
,
b
;
a
=
*
buf
++
;
b
=
*
buf
++
;
printk
(
"%02x%02x "
,
a
,
b
);
}
printk
(
"
\n
"
);
}
for
(
i
=
0
;
i
<
remainder
/
2
;
i
++
)
{
u_char
a
,
b
;
a
=
*
buf
++
;
b
=
*
buf
++
;
printk
(
"%02x%02x "
,
a
,
b
);
}
printk
(
"
\n
"
);
}
#else
#define PRINT_PKT(x...) do { } while(0)
#endif
/* this enables an interrupt in the interrupt mask register */
#define SMC_ENABLE_INT(x) do { \
unsigned long flags; \
unsigned char mask; \
spin_lock_irqsave(&lp->lock, flags); \
mask = SMC_GET_INT_MASK(); \
mask |= (x); \
SMC_SET_INT_MASK(mask); \
spin_unlock_irqrestore(&lp->lock, flags); \
} while (0)
/* this disables an interrupt from the interrupt mask register */
#define SMC_DISABLE_INT(x) do { \
unsigned long flags; \
unsigned char mask; \
spin_lock_irqsave(&lp->lock, flags); \
mask = SMC_GET_INT_MASK(); \
mask &= ~(x); \
SMC_SET_INT_MASK(mask); \
spin_unlock_irqrestore(&lp->lock, flags); \
} while (0)
/*
* Wait while MMU is busy. This is usually in the order of a few nanosecs
* if at all, but let's avoid deadlocking the system if the hardware
* decides to go south.
*/
#define SMC_WAIT_MMU_BUSY() do { \
if (unlikely(SMC_GET_MMU_CMD() & MC_BUSY)) { \
unsigned long timeout = jiffies + 2; \
while (SMC_GET_MMU_CMD() & MC_BUSY) { \
if (time_after(jiffies, timeout)) { \
printk("%s: timeout %s line %d\n", \
dev->name, __FILE__, __LINE__); \
break; \
} \
cpu_relax(); \
} \
} \
} while (0)
/*
* this does a soft reset on the device
*/
static
void
smc_reset
(
struct
net_device
*
dev
)
{
unsigned
long
ioaddr
=
dev
->
base_addr
;
unsigned
int
ctl
,
cfg
;
DBG
(
2
,
"%s: %s
\n
"
,
dev
->
name
,
__FUNCTION__
);
/*
* This resets the registers mostly to defaults, but doesn't
* affect EEPROM. That seems unnecessary
*/
SMC_SELECT_BANK
(
0
);
SMC_SET_RCR
(
RCR_SOFTRST
);
/*
* Setup the Configuration Register
* This is necessary because the CONFIG_REG is not affected
* by a soft reset
*/
SMC_SELECT_BANK
(
1
);
cfg
=
CONFIG_DEFAULT
;
/*
* Setup for fast accesses if requested. If the card/system
* can't handle it then there will be no recovery except for
* a hard reset or power cycle
*/
if
(
nowait
)
cfg
|=
CONFIG_NO_WAIT
;
/*
* Release from possible power-down state
* Configuration register is not affected by Soft Reset
*/
cfg
|=
CONFIG_EPH_POWER_EN
;
SMC_SET_CONFIG
(
cfg
);
/* this should pause enough for the chip to be happy */
/*
* elaborate? What does the chip _need_? --jgarzik
*
* This seems to be undocumented, but something the original
* driver(s) have always done. Suspect undocumented timing
* info/determined empirically. --rmk
*/
udelay
(
1
);
/* Disable transmit and receive functionality */
SMC_SELECT_BANK
(
0
);
SMC_SET_RCR
(
RCR_CLEAR
);
SMC_SET_TCR
(
TCR_CLEAR
);
SMC_SELECT_BANK
(
1
);
ctl
=
SMC_GET_CTL
()
|
CTL_LE_ENABLE
;
/*
* Set the control register to automatically release successfully
* transmitted packets, to make the best use out of our limited
* memory
*/
#if ! THROTTLE_TX_PKTS
ctl
|=
CTL_AUTO_RELEASE
;
#else
ctl
&=
~
CTL_AUTO_RELEASE
;
#endif
SMC_SET_CTL
(
ctl
);
/* Disable all interrupts */
SMC_SELECT_BANK
(
2
);
SMC_SET_INT_MASK
(
0
);
/* Reset the MMU */
SMC_SET_MMU_CMD
(
MC_RESET
);
SMC_WAIT_MMU_BUSY
();
}
/*
* Enable Interrupts, Receive, and Transmit
*/
static
void
smc_enable
(
struct
net_device
*
dev
)
{
unsigned
long
ioaddr
=
dev
->
base_addr
;
struct
smc_local
*
lp
=
netdev_priv
(
dev
);
int
mask
;
DBG
(
2
,
"%s: %s
\n
"
,
dev
->
name
,
__FUNCTION__
);
/* see the header file for options in TCR/RCR DEFAULT */
SMC_SELECT_BANK
(
0
);
SMC_SET_TCR
(
lp
->
tcr_cur_mode
);
SMC_SET_RCR
(
lp
->
rcr_cur_mode
);
/* now, enable interrupts */
mask
=
IM_EPH_INT
|
IM_RX_OVRN_INT
|
IM_RCV_INT
;
if
(
lp
->
version
>=
(
CHIP_91100
<<
4
))
mask
|=
IM_MDINT
;
SMC_SELECT_BANK
(
2
);
SMC_SET_INT_MASK
(
mask
);
}
/*
* this puts the device in an inactive state
*/
static
void
smc_shutdown
(
unsigned
long
ioaddr
)
{
DBG
(
2
,
"%s: %s
\n
"
,
CARDNAME
,
__FUNCTION__
);
/* no more interrupts for me */
SMC_SELECT_BANK
(
2
);
SMC_SET_INT_MASK
(
0
);
/* and tell the card to stay away from that nasty outside world */
SMC_SELECT_BANK
(
0
);
SMC_SET_RCR
(
RCR_CLEAR
);
SMC_SET_TCR
(
TCR_CLEAR
);
#ifdef POWER_DOWN
/* finally, shut the chip down */
SMC_SELECT_BANK
(
1
);
SMC_SET_CONFIG
(
SMC_GET_CONFIG
()
&
~
CONFIG_EPH_POWER_EN
);
#endif
}
/*
* This is the procedure to handle the receipt of a packet.
*/
static
inline
void
smc_rcv
(
struct
net_device
*
dev
)
{
struct
smc_local
*
lp
=
netdev_priv
(
dev
);
unsigned
long
ioaddr
=
dev
->
base_addr
;
unsigned
int
packet_number
,
status
,
packet_len
;
DBG
(
3
,
"%s: %s
\n
"
,
dev
->
name
,
__FUNCTION__
);
packet_number
=
SMC_GET_RXFIFO
();
if
(
unlikely
(
packet_number
&
RXFIFO_REMPTY
))
{
PRINTK
(
"%s: smc_rcv with nothing on FIFO.
\n
"
,
dev
->
name
);
return
;
}
/* read from start of packet */
SMC_SET_PTR
(
PTR_READ
|
PTR_RCV
|
PTR_AUTOINC
);
/* First two words are status and packet length */
SMC_GET_PKT_HDR
(
status
,
packet_len
);
packet_len
&=
0x07ff
;
/* mask off top bits */
DBG
(
2
,
"%s: RX PNR 0x%x STATUS 0x%04x LENGTH 0x%04x (%d)
\n
"
,
dev
->
name
,
packet_number
,
status
,
packet_len
,
packet_len
);
if
(
unlikely
(
status
&
RS_ERRORS
))
{
lp
->
stats
.
rx_errors
++
;
if
(
status
&
RS_ALGNERR
)
lp
->
stats
.
rx_frame_errors
++
;
if
(
status
&
(
RS_TOOSHORT
|
RS_TOOLONG
))
lp
->
stats
.
rx_length_errors
++
;
if
(
status
&
RS_BADCRC
)
lp
->
stats
.
rx_crc_errors
++
;
}
else
{
struct
sk_buff
*
skb
;
unsigned
char
*
data
;
unsigned
int
data_len
;
/* set multicast stats */
if
(
status
&
RS_MULTICAST
)
lp
->
stats
.
multicast
++
;
/*
* Actual payload is packet_len - 4 (or 3 if odd byte).
* We want skb_reserve(2) and the final ctrl word
* (2 bytes, possibly containing the payload odd byte).
* Ence packet_len - 4 + 2 + 2.
*/
skb
=
dev_alloc_skb
(
packet_len
);
if
(
unlikely
(
skb
==
NULL
))
{
printk
(
KERN_NOTICE
"%s: Low memory, packet dropped.
\n
"
,
dev
->
name
);
lp
->
stats
.
rx_dropped
++
;
goto
done
;
}
/* Align IP header to 32 bits */
skb_reserve
(
skb
,
2
);
/* BUG: the LAN91C111 rev A never sets this bit. Force it. */
if
(
lp
->
version
==
0x90
)
status
|=
RS_ODDFRAME
;
/*
* If odd length: packet_len - 3,
* otherwise packet_len - 4.
*/
data_len
=
packet_len
-
((
status
&
RS_ODDFRAME
)
?
3
:
4
);
data
=
skb_put
(
skb
,
data_len
);
SMC_PULL_DATA
(
data
,
packet_len
-
2
);
PRINT_PKT
(
data
,
packet_len
-
2
);
dev
->
last_rx
=
jiffies
;
skb
->
dev
=
dev
;
skb
->
protocol
=
eth_type_trans
(
skb
,
dev
);
netif_rx
(
skb
);
lp
->
stats
.
rx_packets
++
;
lp
->
stats
.
rx_bytes
+=
data_len
;
}
done:
SMC_WAIT_MMU_BUSY
();
SMC_SET_MMU_CMD
(
MC_RELEASE
);
}
/*
* This is called to actually send a packet to the chip.
* Returns non-zero when successful.
*/
static
void
smc_hardware_send_packet
(
struct
net_device
*
dev
)
{
struct
smc_local
*
lp
=
netdev_priv
(
dev
);
unsigned
long
ioaddr
=
dev
->
base_addr
;
struct
sk_buff
*
skb
=
lp
->
saved_skb
;
unsigned
int
packet_no
,
len
;
unsigned
char
*
buf
;
DBG
(
3
,
"%s: %s
\n
"
,
dev
->
name
,
__FUNCTION__
);
packet_no
=
SMC_GET_AR
();
if
(
unlikely
(
packet_no
&
AR_FAILED
))
{
printk
(
"%s: Memory allocation failed.
\n
"
,
dev
->
name
);
lp
->
saved_skb
=
NULL
;
lp
->
stats
.
tx_errors
++
;
lp
->
stats
.
tx_fifo_errors
++
;
dev_kfree_skb_any
(
skb
);
return
;
}
/* point to the beginning of the packet */
SMC_SET_PN
(
packet_no
);
SMC_SET_PTR
(
PTR_AUTOINC
);
buf
=
skb
->
data
;
len
=
skb
->
len
;
DBG
(
2
,
"%s: TX PNR 0x%x LENGTH 0x%04x (%d) BUF 0x%p
\n
"
,
dev
->
name
,
packet_no
,
len
,
len
,
buf
);
PRINT_PKT
(
buf
,
len
);
/*
* Send the packet length (+6 for status words, length, and ctl.
* The card will pad to 64 bytes with zeroes if packet is too small.
*/
SMC_PUT_PKT_HDR
(
0
,
len
+
6
);
/* send the actual data */
SMC_PUSH_DATA
(
buf
,
len
&
~
1
);
/* Send final ctl word with the last byte if there is one */
SMC_outw
(((
len
&
1
)
?
(
0x2000
|
buf
[
len
-
1
])
:
0
),
ioaddr
,
DATA_REG
);
/* and let the chipset deal with it */
SMC_SET_MMU_CMD
(
MC_ENQUEUE
);
SMC_ACK_INT
(
IM_TX_EMPTY_INT
);
dev
->
trans_start
=
jiffies
;
dev_kfree_skb_any
(
skb
);
lp
->
saved_skb
=
NULL
;
lp
->
stats
.
tx_packets
++
;
lp
->
stats
.
tx_bytes
+=
len
;
}
/*
* Since I am not sure if I will have enough room in the chip's ram
* to store the packet, I call this routine which either sends it
* now, or set the card to generates an interrupt when ready
* for the packet.
*/
static
int
smc_hard_start_xmit
(
struct
sk_buff
*
skb
,
struct
net_device
*
dev
)
{
struct
smc_local
*
lp
=
netdev_priv
(
dev
);
unsigned
long
ioaddr
=
dev
->
base_addr
;
unsigned
int
numPages
,
poll_count
,
status
,
saved_bank
;
DBG
(
3
,
"%s: %s
\n
"
,
dev
->
name
,
__FUNCTION__
);
BUG_ON
(
lp
->
saved_skb
!=
NULL
);
lp
->
saved_skb
=
skb
;
/*
* The MMU wants the number of pages to be the number of 256 bytes
* 'pages', minus 1 (since a packet can't ever have 0 pages :))
*
* The 91C111 ignores the size bits, but earlier models don't.
*
* Pkt size for allocating is data length +6 (for additional status
* words, length and ctl)
*
* If odd size then last byte is included in ctl word.
*/
numPages
=
((
skb
->
len
&
~
1
)
+
(
6
-
1
))
>>
8
;
if
(
unlikely
(
numPages
>
7
))
{
printk
(
"%s: Far too big packet error.
\n
"
,
dev
->
name
);
lp
->
saved_skb
=
NULL
;
lp
->
stats
.
tx_errors
++
;
lp
->
stats
.
tx_dropped
++
;
dev_kfree_skb
(
skb
);
return
0
;
}
/* now, try to allocate the memory */
saved_bank
=
SMC_CURRENT_BANK
();
SMC_SELECT_BANK
(
2
);
SMC_SET_MMU_CMD
(
MC_ALLOC
|
numPages
);
/*
* Poll the chip for a short amount of time in case the
* allocation succeeds quickly.
*/
poll_count
=
MEMORY_WAIT_TIME
;
do
{
status
=
SMC_GET_INT
();
if
(
status
&
IM_ALLOC_INT
)
{
SMC_ACK_INT
(
IM_ALLOC_INT
);
break
;
}
}
while
(
--
poll_count
);
if
(
!
poll_count
)
{
/* oh well, wait until the chip finds memory later */
netif_stop_queue
(
dev
);
DBG
(
2
,
"%s: TX memory allocation deferred.
\n
"
,
dev
->
name
);
SMC_ENABLE_INT
(
IM_ALLOC_INT
);
}
else
{
/*
* Allocation succeeded: push packet to the chip's own memory
* immediately.
*
* If THROTTLE_TX_PKTS is selected that means we don't want
* more than a single TX packet taking up space in the chip's
* internal memory at all time, in which case we stop the
* queue right here until we're notified of TX completion.
*
* Otherwise we're quite happy to feed more TX packets right
* away for better TX throughput, in which case the queue is
* left active.
*/
#if THROTTLE_TX_PKTS
netif_stop_queue
(
dev
);
#endif
smc_hardware_send_packet
(
dev
);
SMC_ENABLE_INT
(
IM_TX_INT
|
IM_TX_EMPTY_INT
);
}
SMC_SELECT_BANK
(
saved_bank
);
return
0
;
}
/*
* This handles a TX interrupt, which is only called when:
* - a TX error occurred, or
* - CTL_AUTO_RELEASE is not set and TX of a packet completed.
*/
static
void
smc_tx
(
struct
net_device
*
dev
)
{
unsigned
long
ioaddr
=
dev
->
base_addr
;
struct
smc_local
*
lp
=
netdev_priv
(
dev
);
unsigned
int
saved_packet
,
packet_no
,
tx_status
,
pkt_len
;
DBG
(
3
,
"%s: %s
\n
"
,
dev
->
name
,
__FUNCTION__
);
/* If the TX FIFO is empty then nothing to do */
packet_no
=
SMC_GET_TXFIFO
();
if
(
unlikely
(
packet_no
&
TXFIFO_TEMPTY
))
{
PRINTK
(
"%s: smc_tx with nothing on FIFO.
\n
"
,
dev
->
name
);
return
;
}
/* select packet to read from */
saved_packet
=
SMC_GET_PN
();
SMC_SET_PN
(
packet_no
);
/* read the first word (status word) from this packet */
SMC_SET_PTR
(
PTR_AUTOINC
|
PTR_READ
);
SMC_GET_PKT_HDR
(
tx_status
,
pkt_len
);
DBG
(
2
,
"%s: TX STATUS 0x%04x PNR 0x%02x
\n
"
,
dev
->
name
,
tx_status
,
packet_no
);
if
(
!
(
tx_status
&
TS_SUCCESS
))
lp
->
stats
.
tx_errors
++
;
if
(
tx_status
&
TS_LOSTCAR
)
lp
->
stats
.
tx_carrier_errors
++
;
SMC_WAIT_MMU_BUSY
();
if
(
tx_status
&
TS_LATCOL
)
{
PRINTK
(
"%s: late collision occurred on last xmit
\n
"
,
dev
->
name
);
lp
->
stats
.
tx_window_errors
++
;
/* It's really cheap to requeue the pkt here */
SMC_SET_MMU_CMD
(
MC_ENQUEUE
);
}
else
{
/* kill the packet */
SMC_SET_MMU_CMD
(
MC_FREEPKT
);
}
/* Don't restore Packet Number Reg until busy bit is cleared */
SMC_WAIT_MMU_BUSY
();
SMC_SET_PN
(
saved_packet
);
/* re-enable transmit */
SMC_SELECT_BANK
(
0
);
SMC_SET_TCR
(
lp
->
tcr_cur_mode
);
SMC_SELECT_BANK
(
2
);
}
/*---PHY CONTROL AND CONFIGURATION-----------------------------------------*/
static
void
smc_mii_out
(
struct
net_device
*
dev
,
unsigned
int
val
,
int
bits
)
{
unsigned
long
ioaddr
=
dev
->
base_addr
;
unsigned
int
mii_reg
,
mask
;
mii_reg
=
SMC_GET_MII
()
&
~
(
MII_MCLK
|
MII_MDOE
|
MII_MDO
);
mii_reg
|=
MII_MDOE
;
for
(
mask
=
1
<<
(
bits
-
1
);
mask
;
mask
>>=
1
)
{
if
(
val
&
mask
)
mii_reg
|=
MII_MDO
;
else
mii_reg
&=
~
MII_MDO
;
SMC_SET_MII
(
mii_reg
);
udelay
(
MII_DELAY
);
SMC_SET_MII
(
mii_reg
|
MII_MCLK
);
udelay
(
MII_DELAY
);
}
}
static
unsigned
int
smc_mii_in
(
struct
net_device
*
dev
,
int
bits
)
{
unsigned
long
ioaddr
=
dev
->
base_addr
;
unsigned
int
mii_reg
,
mask
,
val
;
mii_reg
=
SMC_GET_MII
()
&
~
(
MII_MCLK
|
MII_MDOE
|
MII_MDO
);
SMC_SET_MII
(
mii_reg
);
for
(
mask
=
1
<<
(
bits
-
1
),
val
=
0
;
mask
;
mask
>>=
1
)
{
if
(
SMC_GET_MII
()
&
MII_MDI
)
val
|=
mask
;
SMC_SET_MII
(
mii_reg
);
udelay
(
MII_DELAY
);
SMC_SET_MII
(
mii_reg
|
MII_MCLK
);
udelay
(
MII_DELAY
);
}
return
val
;
}
/*
* Reads a register from the MII Management serial interface
*/
static
int
smc_phy_read
(
struct
net_device
*
dev
,
int
phyaddr
,
int
phyreg
)
{
unsigned
long
ioaddr
=
dev
->
base_addr
;
unsigned
int
phydata
,
old_bank
;
/* Save the current bank, and select bank 3 */
old_bank
=
SMC_CURRENT_BANK
();
SMC_SELECT_BANK
(
3
);
/* Idle - 32 ones */
smc_mii_out
(
dev
,
0xffffffff
,
32
);
/* Start code (01) + read (10) + phyaddr + phyreg */
smc_mii_out
(
dev
,
6
<<
10
|
phyaddr
<<
5
|
phyreg
,
14
);
/* Turnaround (2bits) + phydata */
phydata
=
smc_mii_in
(
dev
,
18
);
/* Return to idle state */
SMC_SET_MII
(
SMC_GET_MII
()
&
~
(
MII_MCLK
|
MII_MDOE
|
MII_MDO
));
/* And select original bank */
SMC_SELECT_BANK
(
old_bank
);
DBG
(
3
,
"%s: phyaddr=0x%x, phyreg=0x%x, phydata=0x%x
\n
"
,
__FUNCTION__
,
phyaddr
,
phyreg
,
phydata
);
return
phydata
;
}
/*
* Writes a register to the MII Management serial interface
*/
static
void
smc_phy_write
(
struct
net_device
*
dev
,
int
phyaddr
,
int
phyreg
,
int
phydata
)
{
unsigned
long
ioaddr
=
dev
->
base_addr
;
unsigned
int
old_bank
;
/* Save the current bank, and select bank 3 */
old_bank
=
SMC_CURRENT_BANK
();
SMC_SELECT_BANK
(
3
);
/* Idle - 32 ones */
smc_mii_out
(
dev
,
0xffffffff
,
32
);
/* Start code (01) + write (01) + phyaddr + phyreg + turnaround + phydata */
smc_mii_out
(
dev
,
5
<<
28
|
phyaddr
<<
23
|
phyreg
<<
18
|
2
<<
16
|
phydata
,
32
);
/* Return to idle state */
SMC_SET_MII
(
SMC_GET_MII
()
&
~
(
MII_MCLK
|
MII_MDOE
|
MII_MDO
));
/* And select original bank */
SMC_SELECT_BANK
(
old_bank
);
DBG
(
3
,
"%s: phyaddr=0x%x, phyreg=0x%x, phydata=0x%x
\n
"
,
__FUNCTION__
,
phyaddr
,
phyreg
,
phydata
);
}
/*
* Finds and reports the PHY address
*/
static
void
smc_detect_phy
(
struct
net_device
*
dev
)
{
struct
smc_local
*
lp
=
netdev_priv
(
dev
);
int
phyaddr
;
DBG
(
2
,
"%s: %s
\n
"
,
dev
->
name
,
__FUNCTION__
);
lp
->
phy_type
=
0
;
/*
* Scan all 32 PHY addresses if necessary, starting at
* PHY#1 to PHY#31, and then PHY#0 last.
*/
for
(
phyaddr
=
1
;
phyaddr
<
33
;
++
phyaddr
)
{
unsigned
int
id1
,
id2
;
/* Read the PHY identifiers */
id1
=
smc_phy_read
(
dev
,
phyaddr
&
31
,
MII_PHYSID1
);
id2
=
smc_phy_read
(
dev
,
phyaddr
&
31
,
MII_PHYSID2
);
DBG
(
3
,
"%s: phy_id1=0x%x, phy_id2=0x%x
\n
"
,
dev
->
name
,
id1
,
id2
);
/* Make sure it is a valid identifier */
if
(
id1
!=
0x0000
&&
id1
!=
0xffff
&&
id1
!=
0x8000
&&
id2
!=
0x0000
&&
id2
!=
0xffff
&&
id2
!=
0x8000
)
{
/* Save the PHY's address */
lp
->
mii
.
phy_id
=
phyaddr
&
31
;
lp
->
phy_type
=
id1
<<
16
|
id2
;
break
;
}
}
}
/*
* Sets the PHY to a configuration as determined by the user
*/
static
int
smc_phy_fixed
(
struct
net_device
*
dev
)
{
struct
smc_local
*
lp
=
netdev_priv
(
dev
);
unsigned
long
ioaddr
=
dev
->
base_addr
;
int
phyaddr
=
lp
->
mii
.
phy_id
;
int
bmcr
,
cfg1
;
DBG
(
3
,
"%s: %s
\n
"
,
dev
->
name
,
__FUNCTION__
);
/* Enter Link Disable state */
cfg1
=
smc_phy_read
(
dev
,
phyaddr
,
PHY_CFG1_REG
);
cfg1
|=
PHY_CFG1_LNKDIS
;
smc_phy_write
(
dev
,
phyaddr
,
PHY_CFG1_REG
,
cfg1
);
/*
* Set our fixed capabilities
* Disable auto-negotiation
*/
bmcr
=
0
;
if
(
lp
->
ctl_rfduplx
)
bmcr
|=
BMCR_FULLDPLX
;
if
(
lp
->
ctl_rspeed
==
100
)
bmcr
|=
BMCR_SPEED100
;
/* Write our capabilities to the phy control register */
smc_phy_write
(
dev
,
phyaddr
,
MII_BMCR
,
bmcr
);
/* Re-Configure the Receive/Phy Control register */
SMC_SET_RPC
(
lp
->
rpc_cur_mode
);
return
1
;
}
/*
* smc_phy_reset - reset the phy
* @dev: net device
* @phy: phy address
*
* Issue a software reset for the specified PHY and
* wait up to 100ms for the reset to complete. We should
* not access the PHY for 50ms after issuing the reset.
*
* The time to wait appears to be dependent on the PHY.
*
* Must be called with lp->lock locked.
*/
static
int
smc_phy_reset
(
struct
net_device
*
dev
,
int
phy
)
{
struct
smc_local
*
lp
=
netdev_priv
(
dev
);
unsigned
int
bmcr
;
int
timeout
;
smc_phy_write
(
dev
,
phy
,
MII_BMCR
,
BMCR_RESET
);
for
(
timeout
=
2
;
timeout
;
timeout
--
)
{
spin_unlock_irq
(
&
lp
->
lock
);
msleep
(
50
);
spin_lock_irq
(
&
lp
->
lock
);
bmcr
=
smc_phy_read
(
dev
,
phy
,
MII_BMCR
);
if
(
!
(
bmcr
&
BMCR_RESET
))
break
;
}
return
bmcr
&
BMCR_RESET
;
}
/*
* smc_phy_powerdown - powerdown phy
* @dev: net device
* @phy: phy address
*
* Power down the specified PHY
*/
static
void
smc_phy_powerdown
(
struct
net_device
*
dev
,
int
phy
)
{
struct
smc_local
*
lp
=
netdev_priv
(
dev
);
unsigned
int
bmcr
;
spin_lock_irq
(
&
lp
->
lock
);
bmcr
=
smc_phy_read
(
dev
,
phy
,
MII_BMCR
);
smc_phy_write
(
dev
,
phy
,
MII_BMCR
,
bmcr
|
BMCR_PDOWN
);
spin_unlock_irq
(
&
lp
->
lock
);
}
/*
* smc_phy_check_media - check the media status and adjust TCR
* @dev: net device
* @init: set true for initialisation
*
* Select duplex mode depending on negotiation state. This
* also updates our carrier state.
*/
static
void
smc_phy_check_media
(
struct
net_device
*
dev
,
int
init
)
{
struct
smc_local
*
lp
=
netdev_priv
(
dev
);
unsigned
long
ioaddr
=
dev
->
base_addr
;
if
(
mii_check_media
(
&
lp
->
mii
,
netif_msg_link
(
lp
),
init
))
{
unsigned
int
old_bank
;
/* duplex state has changed */
if
(
lp
->
mii
.
full_duplex
)
{
lp
->
tcr_cur_mode
|=
TCR_SWFDUP
;
}
else
{
lp
->
tcr_cur_mode
&=
~
TCR_SWFDUP
;
}
old_bank
=
SMC_CURRENT_BANK
();
SMC_SELECT_BANK
(
0
);
SMC_SET_TCR
(
lp
->
tcr_cur_mode
);
SMC_SELECT_BANK
(
old_bank
);
}
}
/*
* Configures the specified PHY through the MII management interface
* using Autonegotiation.
* Calls smc_phy_fixed() if the user has requested a certain config.
* If RPC ANEG bit is set, the media selection is dependent purely on
* the selection by the MII (either in the MII BMCR reg or the result
* of autonegotiation.) If the RPC ANEG bit is cleared, the selection
* is controlled by the RPC SPEED and RPC DPLX bits.
*/
static
void
smc_phy_configure
(
struct
net_device
*
dev
)
{
struct
smc_local
*
lp
=
netdev_priv
(
dev
);
unsigned
long
ioaddr
=
dev
->
base_addr
;
int
phyaddr
=
lp
->
mii
.
phy_id
;
int
my_phy_caps
;
/* My PHY capabilities */
int
my_ad_caps
;
/* My Advertised capabilities */
int
status
;
DBG
(
3
,
"%s:smc_program_phy()
\n
"
,
dev
->
name
);
spin_lock_irq
(
&
lp
->
lock
);
/*
* We should not be called if phy_type is zero.
*/
if
(
lp
->
phy_type
==
0
)
goto
smc_phy_configure_exit
;
if
(
smc_phy_reset
(
dev
,
phyaddr
))
{
printk
(
"%s: PHY reset timed out
\n
"
,
dev
->
name
);
goto
smc_phy_configure_exit
;
}
/*
* Enable PHY Interrupts (for register 18)
* Interrupts listed here are disabled
*/
smc_phy_write
(
dev
,
phyaddr
,
PHY_MASK_REG
,
PHY_INT_LOSSSYNC
|
PHY_INT_CWRD
|
PHY_INT_SSD
|
PHY_INT_ESD
|
PHY_INT_RPOL
|
PHY_INT_JAB
|
PHY_INT_SPDDET
|
PHY_INT_DPLXDET
);
/* Configure the Receive/Phy Control register */
SMC_SELECT_BANK
(
0
);
SMC_SET_RPC
(
lp
->
rpc_cur_mode
);
/* If the user requested no auto neg, then go set his request */
if
(
lp
->
mii
.
force_media
)
{
smc_phy_fixed
(
dev
);
goto
smc_phy_configure_exit
;
}
/* Copy our capabilities from MII_BMSR to MII_ADVERTISE */
my_phy_caps
=
smc_phy_read
(
dev
,
phyaddr
,
MII_BMSR
);
if
(
!
(
my_phy_caps
&
BMSR_ANEGCAPABLE
))
{
printk
(
KERN_INFO
"Auto negotiation NOT supported
\n
"
);
smc_phy_fixed
(
dev
);
goto
smc_phy_configure_exit
;
}
my_ad_caps
=
ADVERTISE_CSMA
;
/* I am CSMA capable */
if
(
my_phy_caps
&
BMSR_100BASE4
)
my_ad_caps
|=
ADVERTISE_100BASE4
;
if
(
my_phy_caps
&
BMSR_100FULL
)
my_ad_caps
|=
ADVERTISE_100FULL
;
if
(
my_phy_caps
&
BMSR_100HALF
)
my_ad_caps
|=
ADVERTISE_100HALF
;
if
(
my_phy_caps
&
BMSR_10FULL
)
my_ad_caps
|=
ADVERTISE_10FULL
;
if
(
my_phy_caps
&
BMSR_10HALF
)
my_ad_caps
|=
ADVERTISE_10HALF
;
/* Disable capabilities not selected by our user */
if
(
lp
->
ctl_rspeed
!=
100
)
my_ad_caps
&=
~
(
ADVERTISE_100BASE4
|
ADVERTISE_100FULL
|
ADVERTISE_100HALF
);
if
(
!
lp
->
ctl_rfduplx
)
my_ad_caps
&=
~
(
ADVERTISE_100FULL
|
ADVERTISE_10FULL
);
/* Update our Auto-Neg Advertisement Register */
smc_phy_write
(
dev
,
phyaddr
,
MII_ADVERTISE
,
my_ad_caps
);
lp
->
mii
.
advertising
=
my_ad_caps
;
/*
* Read the register back. Without this, it appears that when
* auto-negotiation is restarted, sometimes it isn't ready and
* the link does not come up.
*/
status
=
smc_phy_read
(
dev
,
phyaddr
,
MII_ADVERTISE
);
DBG
(
2
,
"%s: phy caps=%x
\n
"
,
dev
->
name
,
my_phy_caps
);
DBG
(
2
,
"%s: phy advertised caps=%x
\n
"
,
dev
->
name
,
my_ad_caps
);
/* Restart auto-negotiation process in order to advertise my caps */
smc_phy_write
(
dev
,
phyaddr
,
MII_BMCR
,
BMCR_ANENABLE
|
BMCR_ANRESTART
);
smc_phy_check_media
(
dev
,
1
);
smc_phy_configure_exit:
spin_unlock_irq
(
&
lp
->
lock
);
}
/*
* smc_phy_interrupt
*
* Purpose: Handle interrupts relating to PHY register 18. This is
* called from the "hard" interrupt handler under our private spinlock.
*/
static
void
smc_phy_interrupt
(
struct
net_device
*
dev
)
{
struct
smc_local
*
lp
=
netdev_priv
(
dev
);
int
phyaddr
=
lp
->
mii
.
phy_id
;
int
phy18
;
DBG
(
2
,
"%s: %s
\n
"
,
dev
->
name
,
__FUNCTION__
);
if
(
lp
->
phy_type
==
0
)
return
;
for
(;;)
{
smc_phy_check_media
(
dev
,
0
);
/* Read PHY Register 18, Status Output */
phy18
=
smc_phy_read
(
dev
,
phyaddr
,
PHY_INT_REG
);
if
((
phy18
&
PHY_INT_INT
)
==
0
)
break
;
}
}
/*--- END PHY CONTROL AND CONFIGURATION-------------------------------------*/
static
void
smc_10bt_check_media
(
struct
net_device
*
dev
,
int
init
)
{
struct
smc_local
*
lp
=
netdev_priv
(
dev
);
unsigned
long
ioaddr
=
dev
->
base_addr
;
unsigned
int
old_carrier
,
new_carrier
,
old_bank
;
old_bank
=
SMC_CURRENT_BANK
();
SMC_SELECT_BANK
(
0
);
old_carrier
=
netif_carrier_ok
(
dev
)
?
1
:
0
;
new_carrier
=
SMC_inw
(
ioaddr
,
EPH_STATUS_REG
)
&
ES_LINK_OK
?
1
:
0
;
if
(
init
||
(
old_carrier
!=
new_carrier
))
{
if
(
!
new_carrier
)
{
netif_carrier_off
(
dev
);
}
else
{
netif_carrier_on
(
dev
);
}
if
(
netif_msg_link
(
lp
))
printk
(
KERN_INFO
"%s: link %s
\n
"
,
dev
->
name
,
new_carrier
?
"up"
:
"down"
);
}
SMC_SELECT_BANK
(
old_bank
);
}
static
void
smc_eph_interrupt
(
struct
net_device
*
dev
)
{
unsigned
long
ioaddr
=
dev
->
base_addr
;
unsigned
int
old_bank
,
ctl
;
smc_10bt_check_media
(
dev
,
0
);
old_bank
=
SMC_CURRENT_BANK
();
SMC_SELECT_BANK
(
1
);
ctl
=
SMC_GET_CTL
();
SMC_SET_CTL
(
ctl
&
~
CTL_LE_ENABLE
);
SMC_SET_CTL
(
ctl
);
SMC_SELECT_BANK
(
old_bank
);
}
/*
* This is the main routine of the driver, to handle the device when
* it needs some attention.
*/
static
irqreturn_t
smc_interrupt
(
int
irq
,
void
*
dev_id
,
struct
pt_regs
*
regs
)
{
struct
net_device
*
dev
=
dev_id
;
unsigned
long
ioaddr
=
dev
->
base_addr
;
struct
smc_local
*
lp
=
netdev_priv
(
dev
);
int
status
,
mask
,
timeout
,
card_stats
;
int
saved_bank
,
saved_pointer
;
DBG
(
3
,
"%s: %s
\n
"
,
dev
->
name
,
__FUNCTION__
);
saved_bank
=
SMC_CURRENT_BANK
();
SMC_SELECT_BANK
(
2
);
saved_pointer
=
SMC_GET_PTR
();
mask
=
SMC_GET_INT_MASK
();
SMC_SET_INT_MASK
(
0
);
/* set a timeout value, so I don't stay here forever */
timeout
=
8
;
do
{
status
=
SMC_GET_INT
();
DBG
(
2
,
"%s: IRQ 0x%02x MASK 0x%02x MEM 0x%04x FIFO 0x%04x
\n
"
,
dev
->
name
,
status
,
mask
,
({
int
meminfo
;
SMC_SELECT_BANK
(
0
);
meminfo
=
SMC_GET_MIR
();
SMC_SELECT_BANK
(
2
);
meminfo
;
}),
SMC_GET_FIFO
());
status
&=
mask
;
if
(
!
status
)
break
;
spin_lock
(
&
lp
->
lock
);
if
(
status
&
IM_RCV_INT
)
{
DBG
(
3
,
"%s: RX irq
\n
"
,
dev
->
name
);
smc_rcv
(
dev
);
}
else
if
(
status
&
IM_TX_INT
)
{
DBG
(
3
,
"%s: TX int
\n
"
,
dev
->
name
);
smc_tx
(
dev
);
SMC_ACK_INT
(
IM_TX_INT
);
#if THROTTLE_TX_PKTS
netif_wake_queue
(
dev
);
#endif
}
else
if
(
status
&
IM_ALLOC_INT
)
{
DBG
(
3
,
"%s: Allocation irq
\n
"
,
dev
->
name
);
smc_hardware_send_packet
(
dev
);
mask
|=
(
IM_TX_INT
|
IM_TX_EMPTY_INT
);
mask
&=
~
IM_ALLOC_INT
;
#if ! THROTTLE_TX_PKTS
netif_wake_queue
(
dev
);
#endif
}
else
if
(
status
&
IM_TX_EMPTY_INT
)
{
DBG
(
3
,
"%s: TX empty
\n
"
,
dev
->
name
);
mask
&=
~
IM_TX_EMPTY_INT
;
/* update stats */
SMC_SELECT_BANK
(
0
);
card_stats
=
SMC_GET_COUNTER
();
SMC_SELECT_BANK
(
2
);
/* single collisions */
lp
->
stats
.
collisions
+=
card_stats
&
0xF
;
card_stats
>>=
4
;
/* multiple collisions */
lp
->
stats
.
collisions
+=
card_stats
&
0xF
;
}
else
if
(
status
&
IM_RX_OVRN_INT
)
{
DBG
(
1
,
"%s: RX overrun
\n
"
,
dev
->
name
);
SMC_ACK_INT
(
IM_RX_OVRN_INT
);
lp
->
stats
.
rx_errors
++
;
lp
->
stats
.
rx_fifo_errors
++
;
}
else
if
(
status
&
IM_EPH_INT
)
{
smc_eph_interrupt
(
dev
);
}
else
if
(
status
&
IM_MDINT
)
{
SMC_ACK_INT
(
IM_MDINT
);
smc_phy_interrupt
(
dev
);
}
else
if
(
status
&
IM_ERCV_INT
)
{
SMC_ACK_INT
(
IM_ERCV_INT
);
PRINTK
(
"%s: UNSUPPORTED: ERCV INTERRUPT
\n
"
,
dev
->
name
);
}
spin_unlock
(
&
lp
->
lock
);
}
while
(
--
timeout
);
/* restore register states */
SMC_SET_INT_MASK
(
mask
);
SMC_SET_PTR
(
saved_pointer
);
SMC_SELECT_BANK
(
saved_bank
);
DBG
(
3
,
"%s: Interrupt done (%d loops)
\n
"
,
dev
->
name
,
8
-
timeout
);
/*
* We return IRQ_HANDLED unconditionally here even if there was
* nothing to do. There is a possibility that a packet might
* get enqueued into the chip right after TX_EMPTY_INT is raised
* but just before the CPU acknowledges the IRQ.
* Better take an unneeded IRQ in some occasions than complexifying
* the code for all cases.
*/
return
IRQ_HANDLED
;
}
/* Our watchdog timed out. Called by the networking layer */
static
void
smc_timeout
(
struct
net_device
*
dev
)
{
struct
smc_local
*
lp
=
netdev_priv
(
dev
);
DBG
(
2
,
"%s: %s
\n
"
,
dev
->
name
,
__FUNCTION__
);
smc_reset
(
dev
);
smc_enable
(
dev
);
#if 0
/*
* Reconfiguring the PHY doesn't seem like a bad idea here, but
* it introduced a problem. Now that this is a timeout routine,
* we are getting called from within an interrupt context.
* smc_phy_configure() calls msleep() which calls
* schedule_timeout() which calls schedule(). When schedule()
* is called from an interrupt context, it prints out
* "Scheduling in interrupt" and then calls BUG(). This is
* obviously not desirable. This was worked around by removing
* the call to smc_phy_configure() here because it didn't seem
* absolutely necessary. Ultimately, if msleep() is
* supposed to be usable from an interrupt context (which it
* looks like it thinks it should handle), it should be fixed.
*/
if (lp->phy_type != 0)
smc_phy_configure(dev);
#endif
/* clear anything saved */
if
(
lp
->
saved_skb
!=
NULL
)
{
dev_kfree_skb
(
lp
->
saved_skb
);
lp
->
saved_skb
=
NULL
;
lp
->
stats
.
tx_errors
++
;
lp
->
stats
.
tx_aborted_errors
++
;
}
/* We can accept TX packets again */
dev
->
trans_start
=
jiffies
;
netif_wake_queue
(
dev
);
}
/*
* This sets the internal hardware table to filter out unwanted multicast
* packets before they take up memory.
*
* The SMC chip uses a hash table where the high 6 bits of the CRC of
* address are the offset into the table. If that bit is 1, then the
* multicast packet is accepted. Otherwise, it's dropped silently.
*
* To use the 6 bits as an offset into the table, the high 3 bits are the
* number of the 8 bit register, while the low 3 bits are the bit within
* that register.
*
* This routine is based very heavily on the one provided by Peter Cammaert.
*/
static
void
smc_setmulticast
(
unsigned
long
ioaddr
,
int
count
,
struct
dev_mc_list
*
addrs
)
{
int
i
;
unsigned
char
multicast_table
[
8
];
struct
dev_mc_list
*
cur_addr
;
/* table for flipping the order of 3 bits */
static
unsigned
char
invert3
[]
=
{
0
,
4
,
2
,
6
,
1
,
5
,
3
,
7
};
/* start with a table of all zeros: reject all */
memset
(
multicast_table
,
0
,
sizeof
(
multicast_table
));
cur_addr
=
addrs
;
for
(
i
=
0
;
i
<
count
;
i
++
,
cur_addr
=
cur_addr
->
next
)
{
int
position
;
/* do we have a pointer here? */
if
(
!
cur_addr
)
break
;
/* make sure this is a multicast address - shouldn't this
be a given if we have it here ? */
if
(
!
(
*
cur_addr
->
dmi_addr
&
1
))
continue
;
/* only use the low order bits */
position
=
crc32_le
(
~
0
,
cur_addr
->
dmi_addr
,
6
)
&
0x3f
;
/* do some messy swapping to put the bit in the right spot */
multicast_table
[
invert3
[
position
&
7
]]
|=
(
1
<<
invert3
[(
position
>>
3
)
&
7
]);
}
/* now, the table can be loaded into the chipset */
SMC_SELECT_BANK
(
3
);
SMC_SET_MCAST
(
multicast_table
);
}
/*
* This routine will, depending on the values passed to it,
* either make it accept multicast packets, go into
* promiscuous mode (for TCPDUMP and cousins) or accept
* a select set of multicast packets
*/
static
void
smc_set_multicast_list
(
struct
net_device
*
dev
)
{
struct
smc_local
*
lp
=
netdev_priv
(
dev
);
unsigned
long
ioaddr
=
dev
->
base_addr
;
DBG
(
2
,
"%s: %s
\n
"
,
dev
->
name
,
__FUNCTION__
);
SMC_SELECT_BANK
(
0
);
if
(
dev
->
flags
&
IFF_PROMISC
)
{
DBG
(
2
,
"%s: RCR_PRMS
\n
"
,
dev
->
name
);
lp
->
rcr_cur_mode
|=
RCR_PRMS
;
SMC_SET_RCR
(
lp
->
rcr_cur_mode
);
}
/* BUG? I never disable promiscuous mode if multicasting was turned on.
Now, I turn off promiscuous mode, but I don't do anything to multicasting
when promiscuous mode is turned on.
*/
/*
* Here, I am setting this to accept all multicast packets.
* I don't need to zero the multicast table, because the flag is
* checked before the table is
*/
else
if
(
dev
->
flags
&
IFF_ALLMULTI
||
dev
->
mc_count
>
16
)
{
lp
->
rcr_cur_mode
|=
RCR_ALMUL
;
SMC_SET_RCR
(
lp
->
rcr_cur_mode
);
DBG
(
2
,
"%s: RCR_ALMUL
\n
"
,
dev
->
name
);
}
/*
* We just get all multicast packets even if we only want them
* from one source. This will be changed at some future point.
*/
else
if
(
dev
->
mc_count
)
{
/* support hardware multicasting */
/* be sure I get rid of flags I might have set */
lp
->
rcr_cur_mode
&=
~
(
RCR_PRMS
|
RCR_ALMUL
);
SMC_SET_RCR
(
lp
->
rcr_cur_mode
);
/*
* NOTE: this has to set the bank, so make sure it is the
* last thing called. The bank is set to zero at the top
*/
smc_setmulticast
(
ioaddr
,
dev
->
mc_count
,
dev
->
mc_list
);
}
else
{
DBG
(
2
,
"%s: ~(RCR_PRMS|RCR_ALMUL)
\n
"
,
dev
->
name
);
lp
->
rcr_cur_mode
&=
~
(
RCR_PRMS
|
RCR_ALMUL
);
SMC_SET_RCR
(
lp
->
rcr_cur_mode
);
/*
* since I'm disabling all multicast entirely, I need to
* clear the multicast list
*/
SMC_SELECT_BANK
(
3
);
SMC_CLEAR_MCAST
();
}
}
/*
* Open and Initialize the board
*
* Set up everything, reset the card, etc..
*/
static
int
smc_open
(
struct
net_device
*
dev
)
{
struct
smc_local
*
lp
=
netdev_priv
(
dev
);
unsigned
long
ioaddr
=
dev
->
base_addr
;
DBG
(
2
,
"%s: %s
\n
"
,
dev
->
name
,
__FUNCTION__
);
/*
* Check that the address is valid. If its not, refuse
* to bring the device up. The user must specify an
* address using ifconfig eth0 hw ether xx:xx:xx:xx:xx:xx
*/
if
(
!
is_valid_ether_addr
(
dev
->
dev_addr
))
{
DBG
(
2
,
(
KERN_DEBUG
"smc_open: no valid ethernet hw addr
\n
"
));
return
-
EINVAL
;
}
/* clear out all the junk that was put here before... */
lp
->
saved_skb
=
NULL
;
/* Setup the default Register Modes */
lp
->
tcr_cur_mode
=
TCR_DEFAULT
;
lp
->
rcr_cur_mode
=
RCR_DEFAULT
;
lp
->
rpc_cur_mode
=
RPC_DEFAULT
;
/*
* If we are not using a MII interface, we need to
* monitor our own carrier signal to detect faults.
*/
if
(
lp
->
phy_type
==
0
)
lp
->
tcr_cur_mode
|=
TCR_MON_CSN
;
/* reset the hardware */
smc_reset
(
dev
);
smc_enable
(
dev
);
SMC_SELECT_BANK
(
1
);
SMC_SET_MAC_ADDR
(
dev
->
dev_addr
);
/* Configure the PHY */
if
(
lp
->
phy_type
!=
0
)
smc_phy_configure
(
dev
);
else
{
spin_lock_irq
(
&
lp
->
lock
);
smc_10bt_check_media
(
dev
,
1
);
spin_unlock_irq
(
&
lp
->
lock
);
}
/*
* make sure to initialize the link state with netif_carrier_off()
* somewhere, too --jgarzik
*
* smc_phy_configure() and smc_10bt_check_media() does that. --rmk
*/
netif_start_queue
(
dev
);
return
0
;
}
/*
* smc_close
*
* this makes the board clean up everything that it can
* and not talk to the outside world. Caused by
* an 'ifconfig ethX down'
*/
static
int
smc_close
(
struct
net_device
*
dev
)
{
struct
smc_local
*
lp
=
netdev_priv
(
dev
);
DBG
(
2
,
"%s: %s
\n
"
,
dev
->
name
,
__FUNCTION__
);
netif_stop_queue
(
dev
);
netif_carrier_off
(
dev
);
/* clear everything */
smc_shutdown
(
dev
->
base_addr
);
if
(
lp
->
phy_type
!=
0
)
smc_phy_powerdown
(
dev
,
lp
->
mii
.
phy_id
);
return
0
;
}
/*
* Get the current statistics.
* This may be called with the card open or closed.
*/
static
struct
net_device_stats
*
smc_query_statistics
(
struct
net_device
*
dev
)
{
struct
smc_local
*
lp
=
netdev_priv
(
dev
);
DBG
(
2
,
"%s: %s
\n
"
,
dev
->
name
,
__FUNCTION__
);
return
&
lp
->
stats
;
}
/*
* Ethtool support
*/
static
int
smc_ethtool_getsettings
(
struct
net_device
*
dev
,
struct
ethtool_cmd
*
cmd
)
{
struct
smc_local
*
lp
=
netdev_priv
(
dev
);
int
ret
;
cmd
->
maxtxpkt
=
1
;
cmd
->
maxrxpkt
=
1
;
if
(
lp
->
phy_type
!=
0
)
{
spin_lock_irq
(
&
lp
->
lock
);
ret
=
mii_ethtool_gset
(
&
lp
->
mii
,
cmd
);
spin_unlock_irq
(
&
lp
->
lock
);
}
else
{
cmd
->
supported
=
SUPPORTED_10baseT_Half
|
SUPPORTED_10baseT_Full
|
SUPPORTED_TP
|
SUPPORTED_AUI
;
if
(
lp
->
ctl_rspeed
==
10
)
cmd
->
speed
=
SPEED_10
;
else
if
(
lp
->
ctl_rspeed
==
100
)
cmd
->
speed
=
SPEED_100
;
cmd
->
autoneg
=
AUTONEG_DISABLE
;
cmd
->
transceiver
=
XCVR_INTERNAL
;
cmd
->
port
=
0
;
cmd
->
duplex
=
lp
->
tcr_cur_mode
&
TCR_SWFDUP
?
DUPLEX_FULL
:
DUPLEX_HALF
;
ret
=
0
;
}
return
ret
;
}
static
int
smc_ethtool_setsettings
(
struct
net_device
*
dev
,
struct
ethtool_cmd
*
cmd
)
{
struct
smc_local
*
lp
=
netdev_priv
(
dev
);
int
ret
;
if
(
lp
->
phy_type
!=
0
)
{
spin_lock_irq
(
&
lp
->
lock
);
ret
=
mii_ethtool_sset
(
&
lp
->
mii
,
cmd
);
spin_unlock_irq
(
&
lp
->
lock
);
}
else
{
if
(
cmd
->
autoneg
!=
AUTONEG_DISABLE
||
cmd
->
speed
!=
SPEED_10
||
(
cmd
->
duplex
!=
DUPLEX_HALF
&&
cmd
->
duplex
!=
DUPLEX_FULL
)
||
(
cmd
->
port
!=
PORT_TP
&&
cmd
->
port
!=
PORT_AUI
))
return
-
EINVAL
;
// lp->port = cmd->port;
lp
->
ctl_rfduplx
=
cmd
->
duplex
==
DUPLEX_FULL
;
// if (netif_running(dev))
// smc_set_port(dev);
ret
=
0
;
}
return
ret
;
}
static
void
smc_ethtool_getdrvinfo
(
struct
net_device
*
dev
,
struct
ethtool_drvinfo
*
info
)
{
strncpy
(
info
->
driver
,
CARDNAME
,
sizeof
(
info
->
driver
));
strncpy
(
info
->
version
,
version
,
sizeof
(
info
->
version
));
strncpy
(
info
->
bus_info
,
dev
->
class_dev
.
dev
->
bus_id
,
sizeof
(
info
->
bus_info
));
}
static
int
smc_ethtool_nwayreset
(
struct
net_device
*
dev
)
{
struct
smc_local
*
lp
=
netdev_priv
(
dev
);
int
ret
=
-
EINVAL
;
if
(
lp
->
phy_type
!=
0
)
{
spin_lock_irq
(
&
lp
->
lock
);
ret
=
mii_nway_restart
(
&
lp
->
mii
);
spin_unlock_irq
(
&
lp
->
lock
);
}
return
ret
;
}
static
u32
smc_ethtool_getmsglevel
(
struct
net_device
*
dev
)
{
struct
smc_local
*
lp
=
netdev_priv
(
dev
);
return
lp
->
msg_enable
;
}
static
void
smc_ethtool_setmsglevel
(
struct
net_device
*
dev
,
u32
level
)
{
struct
smc_local
*
lp
=
netdev_priv
(
dev
);
lp
->
msg_enable
=
level
;
}
static
struct
ethtool_ops
smc_ethtool_ops
=
{
.
get_settings
=
smc_ethtool_getsettings
,
.
set_settings
=
smc_ethtool_setsettings
,
.
get_drvinfo
=
smc_ethtool_getdrvinfo
,
.
get_msglevel
=
smc_ethtool_getmsglevel
,
.
set_msglevel
=
smc_ethtool_setmsglevel
,
.
nway_reset
=
smc_ethtool_nwayreset
,
.
get_link
=
ethtool_op_get_link
,
// .get_eeprom = smc_ethtool_geteeprom,
// .set_eeprom = smc_ethtool_seteeprom,
};
/*
* smc_findirq
*
* This routine has a simple purpose -- make the SMC chip generate an
* interrupt, so an auto-detect routine can detect it, and find the IRQ,
*/
/*
* does this still work?
*
* I just deleted auto_irq.c, since it was never built...
* --jgarzik
*/
static
int
__init
smc_findirq
(
unsigned
long
ioaddr
)
{
int
timeout
=
20
;
unsigned
long
cookie
;
DBG
(
2
,
"%s: %s
\n
"
,
CARDNAME
,
__FUNCTION__
);
cookie
=
probe_irq_on
();
/*
* What I try to do here is trigger an ALLOC_INT. This is done
* by allocating a small chunk of memory, which will give an interrupt
* when done.
*/
/* enable ALLOCation interrupts ONLY */
SMC_SELECT_BANK
(
2
);
SMC_SET_INT_MASK
(
IM_ALLOC_INT
);
/*
* Allocate 512 bytes of memory. Note that the chip was just
* reset so all the memory is available
*/
SMC_SET_MMU_CMD
(
MC_ALLOC
|
1
);
/*
* Wait until positive that the interrupt has been generated
*/
do
{
int
int_status
;
udelay
(
10
);
int_status
=
SMC_GET_INT
();
if
(
int_status
&
IM_ALLOC_INT
)
break
;
/* got the interrupt */
}
while
(
--
timeout
);
/*
* there is really nothing that I can do here if timeout fails,
* as autoirq_report will return a 0 anyway, which is what I
* want in this case. Plus, the clean up is needed in both
* cases.
*/
/* and disable all interrupts again */
SMC_SET_INT_MASK
(
0
);
/* and return what I found */
return
probe_irq_off
(
cookie
);
}
/*
* Function: smc_probe(unsigned long ioaddr)
*
* Purpose:
* Tests to see if a given ioaddr points to an SMC91x chip.
* Returns a 0 on success
*
* Algorithm:
* (1) see if the high byte of BANK_SELECT is 0x33
* (2) compare the ioaddr with the base register's address
* (3) see if I recognize the chip ID in the appropriate register
*
* Here I do typical initialization tasks.
*
* o Initialize the structure if needed
* o print out my vanity message if not done so already
* o print out what type of hardware is detected
* o print out the ethernet address
* o find the IRQ
* o set up my private data
* o configure the dev structure with my subroutines
* o actually GRAB the irq.
* o GRAB the region
*/
static
int
__init
smc_probe
(
struct
net_device
*
dev
,
unsigned
long
ioaddr
)
{
struct
smc_local
*
lp
=
netdev_priv
(
dev
);
static
int
version_printed
=
0
;
int
i
,
retval
;
unsigned
int
val
,
revision_register
;
const
char
*
version_string
;
DBG
(
2
,
"%s: %s
\n
"
,
CARDNAME
,
__FUNCTION__
);
/* First, see if the high byte is 0x33 */
val
=
SMC_CURRENT_BANK
();
DBG
(
2
,
"%s: bank signature probe returned 0x%04x
\n
"
,
CARDNAME
,
val
);
if
((
val
&
0xFF00
)
!=
0x3300
)
{
if
((
val
&
0xFF
)
==
0x33
)
{
printk
(
KERN_WARNING
"%s: Detected possible byte-swapped interface"
" at IOADDR 0x%lx
\n
"
,
CARDNAME
,
ioaddr
);
}
retval
=
-
ENODEV
;
goto
err_out
;
}
/*
* The above MIGHT indicate a device, but I need to write to
* further test this.
*/
SMC_SELECT_BANK
(
0
);
val
=
SMC_CURRENT_BANK
();
if
((
val
&
0xFF00
)
!=
0x3300
)
{
retval
=
-
ENODEV
;
goto
err_out
;
}
/*
* well, we've already written once, so hopefully another
* time won't hurt. This time, I need to switch the bank
* register to bank 1, so I can access the base address
* register
*/
SMC_SELECT_BANK
(
1
);
val
=
SMC_GET_BASE
();
val
=
((
val
&
0x1F00
)
>>
3
)
<<
SMC_IO_SHIFT
;
if
((
ioaddr
&
((
PAGE_SIZE
-
1
)
<<
SMC_IO_SHIFT
))
!=
val
)
{
printk
(
"%s: IOADDR %lx doesn't match configuration (%x).
\n
"
,
CARDNAME
,
ioaddr
,
val
);
}
/*
* check if the revision register is something that I
* recognize. These might need to be added to later,
* as future revisions could be added.
*/
SMC_SELECT_BANK
(
3
);
revision_register
=
SMC_GET_REV
();
DBG
(
2
,
"%s: revision = 0x%04x
\n
"
,
CARDNAME
,
revision_register
);
version_string
=
chip_ids
[
(
revision_register
>>
4
)
&
0xF
];
if
(
!
version_string
||
(
revision_register
&
0xff00
)
!=
0x3300
)
{
/* I don't recognize this chip, so... */
printk
(
"%s: IO 0x%lx: Unrecognized revision register 0x%04x"
", Contact author.
\n
"
,
CARDNAME
,
ioaddr
,
revision_register
);
retval
=
-
ENODEV
;
goto
err_out
;
}
/* At this point I'll assume that the chip is an SMC91x. */
if
(
version_printed
++
==
0
)
printk
(
"%s"
,
version
);
/* fill in some of the fields */
dev
->
base_addr
=
ioaddr
;
lp
->
version
=
revision_register
&
0xff
;
/* Get the MAC address */
SMC_SELECT_BANK
(
1
);
SMC_GET_MAC_ADDR
(
dev
->
dev_addr
);
/* now, reset the chip, and put it into a known state */
smc_reset
(
dev
);
/*
* If dev->irq is 0, then the device has to be banged on to see
* what the IRQ is.
*
* This banging doesn't always detect the IRQ, for unknown reasons.
* a workaround is to reset the chip and try again.
*
* Interestingly, the DOS packet driver *SETS* the IRQ on the card to
* be what is requested on the command line. I don't do that, mostly
* because the card that I have uses a non-standard method of accessing
* the IRQs, and because this _should_ work in most configurations.
*
* Specifying an IRQ is done with the assumption that the user knows
* what (s)he is doing. No checking is done!!!!
*/
if
(
dev
->
irq
<
1
)
{
int
trials
;
trials
=
3
;
while
(
trials
--
)
{
dev
->
irq
=
smc_findirq
(
ioaddr
);
if
(
dev
->
irq
)
break
;
/* kick the card and try again */
smc_reset
(
dev
);
}
}
if
(
dev
->
irq
==
0
)
{
printk
(
"%s: Couldn't autodetect your IRQ. Use irq=xx.
\n
"
,
dev
->
name
);
retval
=
-
ENODEV
;
goto
err_out
;
}
dev
->
irq
=
irq_canonicalize
(
dev
->
irq
);
/* Fill in the fields of the device structure with ethernet values. */
ether_setup
(
dev
);
dev
->
open
=
smc_open
;
dev
->
stop
=
smc_close
;
dev
->
hard_start_xmit
=
smc_hard_start_xmit
;
dev
->
tx_timeout
=
smc_timeout
;
dev
->
watchdog_timeo
=
msecs_to_jiffies
(
watchdog
);
dev
->
get_stats
=
smc_query_statistics
;
dev
->
set_multicast_list
=
smc_set_multicast_list
;
dev
->
ethtool_ops
=
&
smc_ethtool_ops
;
spin_lock_init
(
&
lp
->
lock
);
lp
->
mii
.
phy_id_mask
=
0x1f
;
lp
->
mii
.
reg_num_mask
=
0x1f
;
lp
->
mii
.
force_media
=
0
;
lp
->
mii
.
full_duplex
=
0
;
lp
->
mii
.
dev
=
dev
;
lp
->
mii
.
mdio_read
=
smc_phy_read
;
lp
->
mii
.
mdio_write
=
smc_phy_write
;
/*
* Locate the phy, if any.
*/
if
(
lp
->
version
>=
(
CHIP_91100
<<
4
))
smc_detect_phy
(
dev
);
/* Set default parameters */
lp
->
msg_enable
=
NETIF_MSG_LINK
;
lp
->
ctl_rfduplx
=
0
;
lp
->
ctl_rspeed
=
10
;
if
(
lp
->
version
>=
(
CHIP_91100
<<
4
))
{
lp
->
ctl_rfduplx
=
1
;
lp
->
ctl_rspeed
=
100
;
}
/* Grab the IRQ */
retval
=
request_irq
(
dev
->
irq
,
&
smc_interrupt
,
0
,
dev
->
name
,
dev
);
if
(
retval
)
goto
err_out
;
set_irq_type
(
dev
->
irq
,
IRQT_RISING
);
#ifdef SMC_USE_PXA_DMA
{
int
dma
=
pxa_request_dma
(
dev
->
name
,
DMA_PRIO_LOW
,
smc_pxa_dma_irq
,
NULL
);
if
(
dma
>=
0
)
dev
->
dma
=
dma
;
}
#endif
retval
=
register_netdev
(
dev
);
if
(
retval
==
0
)
{
/* now, print out the card info, in a short format.. */
printk
(
"%s: %s (rev %d) at %#lx IRQ %d"
,
dev
->
name
,
version_string
,
revision_register
&
0x0f
,
dev
->
base_addr
,
dev
->
irq
);
if
(
dev
->
dma
!=
(
unsigned
char
)
-
1
)
printk
(
" DMA %d"
,
dev
->
dma
);
printk
(
"%s%s
\n
"
,
nowait
?
" [nowait]"
:
""
,
THROTTLE_TX_PKTS
?
" [throttle_tx]"
:
""
);
if
(
!
is_valid_ether_addr
(
dev
->
dev_addr
))
{
printk
(
"%s: Invalid ethernet MAC address. Please "
"set using ifconfig
\n
"
,
dev
->
name
);
}
else
{
/* Print the Ethernet address */
printk
(
"%s: Ethernet addr: "
,
dev
->
name
);
for
(
i
=
0
;
i
<
5
;
i
++
)
printk
(
"%2.2x:"
,
dev
->
dev_addr
[
i
]);
printk
(
"%2.2x
\n
"
,
dev
->
dev_addr
[
5
]);
}
if
(
lp
->
phy_type
==
0
)
{
PRINTK
(
"%s: No PHY found
\n
"
,
dev
->
name
);
}
else
if
((
lp
->
phy_type
&
0xfffffff0
)
==
0x0016f840
)
{
PRINTK
(
"%s: PHY LAN83C183 (LAN91C111 Internal)
\n
"
,
dev
->
name
);
}
else
if
((
lp
->
phy_type
&
0xfffffff0
)
==
0x02821c50
)
{
PRINTK
(
"%s: PHY LAN83C180
\n
"
,
dev
->
name
);
}
}
err_out:
#ifdef SMC_USE_PXA_DMA
if
(
retval
&&
dev
->
dma
!=
(
unsigned
char
)
-
1
)
pxa_free_dma
(
dev
->
dma
);
#endif
return
retval
;
}
static
int
smc_enable_device
(
unsigned
long
attrib_phys
)
{
unsigned
long
flags
;
unsigned
char
ecor
,
ecsr
;
void
*
addr
;
/*
* Map the attribute space. This is overkill, but clean.
*/
addr
=
ioremap
(
attrib_phys
,
ATTRIB_SIZE
);
if
(
!
addr
)
return
-
ENOMEM
;
/*
* Reset the device. We must disable IRQs around this
* since a reset causes the IRQ line become active.
*/
local_irq_save
(
flags
);
ecor
=
readb
(
addr
+
(
ECOR
<<
SMC_IO_SHIFT
))
&
~
ECOR_RESET
;
writeb
(
ecor
|
ECOR_RESET
,
addr
+
(
ECOR
<<
SMC_IO_SHIFT
));
readb
(
addr
+
(
ECOR
<<
SMC_IO_SHIFT
));
/*
* Wait 100us for the chip to reset.
*/
udelay
(
100
);
/*
* The device will ignore all writes to the enable bit while
* reset is asserted, even if the reset bit is cleared in the
* same write. Must clear reset first, then enable the device.
*/
writeb
(
ecor
,
addr
+
(
ECOR
<<
SMC_IO_SHIFT
));
writeb
(
ecor
|
ECOR_ENABLE
,
addr
+
(
ECOR
<<
SMC_IO_SHIFT
));
/*
* Set the appropriate byte/word mode.
*/
ecsr
=
readb
(
addr
+
(
ECSR
<<
SMC_IO_SHIFT
))
&
~
ECSR_IOIS8
;
#ifndef SMC_CAN_USE_16BIT
ecsr
|=
ECSR_IOIS8
;
#endif
writeb
(
ecsr
,
addr
+
(
ECSR
<<
SMC_IO_SHIFT
));
local_irq_restore
(
flags
);
iounmap
(
addr
);
/*
* Wait for the chip to wake up. We could poll the control
* register in the main register space, but that isn't mapped
* yet. We know this is going to take 750us.
*/
msleep
(
1
);
return
0
;
}
/*
* smc_init(void)
* Input parameters:
* dev->base_addr == 0, try to find all possible locations
* dev->base_addr > 0x1ff, this is the address to check
* dev->base_addr == <anything else>, return failure code
*
* Output:
* 0 --> there is a device
* anything else, error
*/
static
int
smc_drv_probe
(
struct
device
*
dev
)
{
struct
platform_device
*
pdev
=
to_platform_device
(
dev
);
struct
net_device
*
ndev
;
struct
resource
*
res
,
*
ext
=
NULL
;
unsigned
int
*
addr
;
int
ret
;
res
=
platform_get_resource
(
pdev
,
IORESOURCE_MEM
,
0
);
if
(
!
res
)
{
ret
=
-
ENODEV
;
goto
out
;
}
/*
* Request the regions.
*/
if
(
!
request_mem_region
(
res
->
start
,
SMC_IO_EXTENT
,
"smc91x"
))
{
ret
=
-
EBUSY
;
goto
out
;
}
ndev
=
alloc_etherdev
(
sizeof
(
struct
smc_local
));
if
(
!
ndev
)
{
printk
(
"%s: could not allocate device.
\n
"
,
CARDNAME
);
ret
=
-
ENOMEM
;
goto
release_1
;
}
SET_MODULE_OWNER
(
ndev
);
SET_NETDEV_DEV
(
ndev
,
dev
);
ndev
->
dma
=
(
unsigned
char
)
-
1
;
ndev
->
irq
=
platform_get_irq
(
pdev
,
0
);
ext
=
platform_get_resource
(
pdev
,
IORESOURCE_MEM
,
1
);
if
(
ext
)
{
if
(
!
request_mem_region
(
ext
->
start
,
ATTRIB_SIZE
,
ndev
->
name
))
{
ret
=
-
EBUSY
;
goto
release_1
;
}
#if defined(CONFIG_SA1100_ASSABET)
NCR_0
|=
NCR_ENET_OSC_EN
;
#endif
ret
=
smc_enable_device
(
ext
->
start
);
if
(
ret
)
goto
release_both
;
}
addr
=
ioremap
(
res
->
start
,
SMC_IO_EXTENT
);
if
(
!
addr
)
{
ret
=
-
ENOMEM
;
goto
release_both
;
}
dev_set_drvdata
(
dev
,
ndev
);
ret
=
smc_probe
(
ndev
,
(
unsigned
long
)
addr
);
if
(
ret
!=
0
)
{
dev_set_drvdata
(
dev
,
NULL
);
iounmap
(
addr
);
release_both:
if
(
ext
)
release_mem_region
(
ext
->
start
,
ATTRIB_SIZE
);
free_netdev
(
ndev
);
release_1:
release_mem_region
(
res
->
start
,
SMC_IO_EXTENT
);
out:
printk
(
"%s: not found (%d).
\n
"
,
CARDNAME
,
ret
);
}
#ifdef SMC_USE_PXA_DMA
else
{
struct
smc_local
*
lp
=
netdev_priv
(
ndev
);
lp
->
physaddr
=
res
->
start
;
}
#endif
return
ret
;
}
static
int
smc_drv_remove
(
struct
device
*
dev
)
{
struct
platform_device
*
pdev
=
to_platform_device
(
dev
);
struct
net_device
*
ndev
=
dev_get_drvdata
(
dev
);
struct
resource
*
res
;
dev_set_drvdata
(
dev
,
NULL
);
unregister_netdev
(
ndev
);
free_irq
(
ndev
->
irq
,
ndev
);
#ifdef SMC_USE_PXA_DMA
if
(
ndev
->
dma
!=
(
unsigned
char
)
-
1
)
pxa_free_dma
(
ndev
->
dma
);
#endif
iounmap
((
void
*
)
ndev
->
base_addr
);
res
=
platform_get_resource
(
pdev
,
IORESOURCE_MEM
,
1
);
if
(
res
)
release_mem_region
(
res
->
start
,
ATTRIB_SIZE
);
res
=
platform_get_resource
(
pdev
,
IORESOURCE_MEM
,
0
);
release_mem_region
(
res
->
start
,
SMC_IO_EXTENT
);
free_netdev
(
ndev
);
return
0
;
}
static
int
smc_drv_suspend
(
struct
device
*
dev
,
u32
state
,
u32
level
)
{
struct
net_device
*
ndev
=
dev_get_drvdata
(
dev
);
if
(
ndev
&&
level
==
SUSPEND_DISABLE
)
{
if
(
netif_running
(
ndev
))
{
netif_device_detach
(
ndev
);
smc_shutdown
(
ndev
->
base_addr
);
}
}
return
0
;
}
static
int
smc_drv_resume
(
struct
device
*
dev
,
u32
level
)
{
struct
platform_device
*
pdev
=
to_platform_device
(
dev
);
struct
net_device
*
ndev
=
dev_get_drvdata
(
dev
);
if
(
ndev
&&
level
==
RESUME_ENABLE
)
{
struct
smc_local
*
lp
=
netdev_priv
(
ndev
);
unsigned
long
ioaddr
=
ndev
->
base_addr
;
if
(
pdev
->
num_resources
==
3
)
smc_enable_device
(
pdev
->
resource
[
2
].
start
);
if
(
netif_running
(
ndev
))
{
smc_reset
(
ndev
);
smc_enable
(
ndev
);
SMC_SELECT_BANK
(
1
);
SMC_SET_MAC_ADDR
(
ndev
->
dev_addr
);
if
(
lp
->
phy_type
!=
0
)
smc_phy_configure
(
ndev
);
netif_device_attach
(
ndev
);
}
}
return
0
;
}
static
struct
device_driver
smc_driver
=
{
.
name
=
CARDNAME
,
.
bus
=
&
platform_bus_type
,
.
probe
=
smc_drv_probe
,
.
remove
=
smc_drv_remove
,
.
suspend
=
smc_drv_suspend
,
.
resume
=
smc_drv_resume
,
};
static
int
__init
smc_init
(
void
)
{
#ifdef MODULE
if
(
io
==
-
1
)
printk
(
KERN_WARNING
"%s: You shouldn't use auto-probing with insmod!
\n
"
,
CARDNAME
);
#endif
return
driver_register
(
&
smc_driver
);
}
static
void
__exit
smc_cleanup
(
void
)
{
driver_unregister
(
&
smc_driver
);
}
module_init
(
smc_init
);
module_exit
(
smc_cleanup
);
drivers/net/arm/smc91x.h
0 → 100644
View file @
aa6af1e1
/*------------------------------------------------------------------------
. smc91x.h - macros for SMSC's 91C9x/91C1xx single-chip Ethernet device.
.
. Copyright (C) 1996 by Erik Stahlman
. Copyright (C) 2001 Standard Microsystems Corporation
. Developed by Simple Network Magic Corporation
. Copyright (C) 2003 Monta Vista Software, Inc.
. Unified SMC91x driver by Nicolas Pitre
.
. This program is free software; you can redistribute it and/or modify
. it under the terms of the GNU General Public License as published by
. the Free Software Foundation; either version 2 of the License, or
. (at your option) any later version.
.
. This program is distributed in the hope that it will be useful,
. but WITHOUT ANY WARRANTY; without even the implied warranty of
. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
. GNU General Public License for more details.
.
. You should have received a copy of the GNU General Public License
. along with this program; if not, write to the Free Software
. Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
.
. Information contained in this file was obtained from the LAN91C111
. manual from SMC. To get a copy, if you really want one, you can find
. information under www.smsc.com.
.
. Authors
. Erik Stahlman <erik@vt.edu>
. Daris A Nevil <dnevil@snmc.com>
. Nicolas Pitre <nico@cam.org>
.
---------------------------------------------------------------------------*/
#ifndef _SMC91X_H_
#define _SMC91X_H_
/*
* Define your architecture specific bus configuration parameters here.
*/
#if defined(CONFIG_SA1100_GRAPHICSCLIENT) || \
defined(CONFIG_SA1100_PFS168) || \
defined(CONFIG_SA1100_FLEXANET) || \
defined(CONFIG_SA1100_GRAPHICSMASTER) || \
defined(CONFIG_ARCH_LUBBOCK)
/* We can only do 16-bit reads and writes in the static memory space. */
#define SMC_CAN_USE_8BIT 0
#define SMC_CAN_USE_16BIT 1
#define SMC_CAN_USE_32BIT 0
#define SMC_NOWAIT 1
/* The first two address lines aren't connected... */
#define SMC_IO_SHIFT 2
#define SMC_inw(a, r) readw((a) + (r))
#define SMC_outw(v, a, r) writew(v, (a) + (r))
#define SMC_insw(a, r, p, l) readsw((a) + (r), p, l)
#define SMC_outsw(a, r, p, l) writesw((a) + (r), p, l)
#elif defined(CONFIG_SA1100_ASSABET)
#include <asm/arch/neponset.h>
/* We can only do 8-bit reads and writes in the static memory space. */
#define SMC_CAN_USE_8BIT 1
#define SMC_CAN_USE_16BIT 0
#define SMC_CAN_USE_32BIT 0
#define SMC_NOWAIT 1
/* The first two address lines aren't connected... */
#define SMC_IO_SHIFT 2
#define SMC_inb(a, r) readb((a) + (r))
#define SMC_outb(v, a, r) writeb(v, (a) + (r))
#define SMC_insb(a, r, p, l) readsb((a) + (r), p, (l))
#define SMC_outsb(a, r, p, l) writesb((a) + (r), p, (l))
#elif defined(CONFIG_ARCH_INNOKOM) || \
defined(CONFIG_MACH_MAINSTONE) || \
defined(CONFIG_ARCH_PXA_IDP) || \
defined(CONFIG_ARCH_RAMSES)
#define SMC_CAN_USE_8BIT 1
#define SMC_CAN_USE_16BIT 1
#define SMC_CAN_USE_32BIT 1
#define SMC_IO_SHIFT 0
#define SMC_NOWAIT 1
#define SMC_USE_PXA_DMA 1
#define SMC_inb(a, r) readb((a) + (r))
#define SMC_inw(a, r) readw((a) + (r))
#define SMC_inl(a, r) readl((a) + (r))
#define SMC_outb(v, a, r) writeb(v, (a) + (r))
#define SMC_outl(v, a, r) writel(v, (a) + (r))
#define SMC_insl(a, r, p, l) readsl((a) + (r), p, l)
#define SMC_outsl(a, r, p, l) writesl((a) + (r), p, l)
/* We actually can't write halfwords properly if not word aligned */
static
inline
void
SMC_outw
(
u16
val
,
unsigned
long
ioaddr
,
int
reg
)
{
if
(
reg
&
2
)
{
unsigned
int
v
=
val
<<
16
;
v
|=
readl
(
ioaddr
+
(
reg
&
~
2
))
&
0xffff
;
writel
(
v
,
ioaddr
+
(
reg
&
~
2
));
}
else
{
writew
(
val
,
ioaddr
+
reg
);
}
}
#elif defined(CONFIG_ISA)
#define SMC_CAN_USE_8BIT 1
#define SMC_CAN_USE_16BIT 1
#define SMC_CAN_USE_32BIT 0
#define SMC_inb(a, r) inb((a) + (r))
#define SMC_inw(a, r) inw((a) + (r))
#define SMC_outb(v, a, r) outb(v, (a) + (r))
#define SMC_outw(v, a, r) outw(v, (a) + (r))
#define SMC_insw(a, r, p, l) insw((a) + (r), p, l)
#define SMC_outsw(a, r, p, l) outsw((a) + (r), p, l)
#else
#define SMC_CAN_USE_8BIT 1
#define SMC_CAN_USE_16BIT 1
#define SMC_CAN_USE_32BIT 1
#define SMC_NOWAIT 1
#define SMC_inb(a, r) readb((a) + (r))
#define SMC_inw(a, r) readw((a) + (r))
#define SMC_inl(a, r) readl((a) + (r))
#define SMC_outb(v, a, r) writeb(v, (a) + (r))
#define SMC_outw(v, a, r) writew(v, (a) + (r))
#define SMC_outl(v, a, r) writel(v, (a) + (r))
#define SMC_insl(a, r, p, l) readsl((a) + (r), p, l)
#define SMC_outsl(a, r, p, l) writesl((a) + (r), p, l)
#define RPC_LSA_DEFAULT RPC_LED_100_10
#define RPC_LSB_DEFAULT RPC_LED_TX_RX
#endif
#ifdef SMC_USE_PXA_DMA
/*
* Let's use the DMA engine on the XScale PXA2xx for RX packets. This is
* always happening in irq context so no need to worry about races. TX is
* different and probably not worth it for that reason, and not as critical
* as RX which can overrun memory and lose packets.
*/
#include <linux/pci.h>
#include <asm/dma.h>
#ifdef SMC_insl
#undef SMC_insl
#define SMC_insl(a, r, p, l) \
smc_pxa_dma_insl(a, lp->physaddr, r, dev->dma, p, l)
static
inline
void
smc_pxa_dma_insl
(
u_long
ioaddr
,
u_long
physaddr
,
int
reg
,
int
dma
,
u_char
*
buf
,
int
len
)
{
dma_addr_t
dmabuf
;
/* fallback if no DMA available */
if
(
dma
==
(
unsigned
char
)
-
1
)
{
readsl
(
ioaddr
+
reg
,
buf
,
len
);
return
;
}
/* 64 bit alignment is required for memory to memory DMA */
if
((
long
)
buf
&
4
)
{
*
((
u32
*
)
buf
)
++
=
SMC_inl
(
ioaddr
,
reg
);
len
--
;
}
len
*=
4
;
dmabuf
=
dma_map_single
(
NULL
,
buf
,
len
,
PCI_DMA_FROMDEVICE
);
DCSR
(
dma
)
=
DCSR_NODESC
;
DTADR
(
dma
)
=
dmabuf
;
DSADR
(
dma
)
=
physaddr
+
reg
;
DCMD
(
dma
)
=
(
DCMD_INCTRGADDR
|
DCMD_BURST32
|
DCMD_WIDTH4
|
(
DCMD_LENGTH
&
len
));
DCSR
(
dma
)
=
DCSR_NODESC
|
DCSR_RUN
;
while
(
!
(
DCSR
(
dma
)
&
DCSR_STOPSTATE
));
DCSR
(
dma
)
=
0
;
dma_unmap_single
(
NULL
,
dmabuf
,
len
,
PCI_DMA_FROMDEVICE
);
}
#endif
#ifdef SMC_insw
#undef SMC_insw
#define SMC_insw(a, r, p, l) \
smc_pxa_dma_insw(a, lp->physaddr, r, dev->dma, p, l)
static
inline
void
smc_pxa_dma_insw
(
u_long
ioaddr
,
u_long
physaddr
,
int
reg
,
int
dma
,
u_char
*
buf
,
int
len
)
{
dma_addr_t
dmabuf
;
/* fallback if no DMA available */
if
(
dma
==
(
unsigned
char
)
-
1
)
{
readsw
(
ioaddr
+
reg
,
buf
,
len
);
return
;
}
/* 64 bit alignment is required for memory to memory DMA */
while
((
long
)
buf
&
6
)
{
*
((
u16
*
)
buf
)
++
=
SMC_inw
(
ioaddr
,
reg
);
len
--
;
}
len
*=
2
;
dmabuf
=
dma_map_single
(
NULL
,
buf
,
len
,
PCI_DMA_FROMDEVICE
);
DCSR
(
dma
)
=
DCSR_NODESC
;
DTADR
(
dma
)
=
dmabuf
;
DSADR
(
dma
)
=
physaddr
+
reg
;
DCMD
(
dma
)
=
(
DCMD_INCTRGADDR
|
DCMD_BURST32
|
DCMD_WIDTH2
|
(
DCMD_LENGTH
&
len
));
DCSR
(
dma
)
=
DCSR_NODESC
|
DCSR_RUN
;
while
(
!
(
DCSR
(
dma
)
&
DCSR_STOPSTATE
));
DCSR
(
dma
)
=
0
;
dma_unmap_single
(
NULL
,
dmabuf
,
len
,
PCI_DMA_FROMDEVICE
);
}
#endif
static
void
smc_pxa_dma_irq
(
int
dma
,
void
*
dummy
,
struct
pt_regs
*
regs
)
{
DCSR
(
dma
)
=
0
;
}
#endif
/* SMC_USE_PXA_DMA */
/* Because of bank switching, the LAN91x uses only 16 I/O ports */
#ifndef SMC_IO_SHIFT
#define SMC_IO_SHIFT 0
#endif
#define SMC_IO_EXTENT (16 << SMC_IO_SHIFT)
/*
. Bank Select Register:
.
. yyyy yyyy 0000 00xx
. xx = bank number
. yyyy yyyy = 0x33, for identification purposes.
*/
#define BANK_SELECT (14 << SMC_IO_SHIFT)
// Transmit Control Register
/* BANK 0 */
#define TCR_REG SMC_REG(0x0000, 0)
#define TCR_ENABLE 0x0001 // When 1 we can transmit
#define TCR_LOOP 0x0002 // Controls output pin LBK
#define TCR_FORCOL 0x0004 // When 1 will force a collision
#define TCR_PAD_EN 0x0080 // When 1 will pad tx frames < 64 bytes w/0
#define TCR_NOCRC 0x0100 // When 1 will not append CRC to tx frames
#define TCR_MON_CSN 0x0400 // When 1 tx monitors carrier
#define TCR_FDUPLX 0x0800 // When 1 enables full duplex operation
#define TCR_STP_SQET 0x1000 // When 1 stops tx if Signal Quality Error
#define TCR_EPH_LOOP 0x2000 // When 1 enables EPH block loopback
#define TCR_SWFDUP 0x8000 // When 1 enables Switched Full Duplex mode
#define TCR_CLEAR 0
/* do NOTHING */
/* the default settings for the TCR register : */
#define TCR_DEFAULT (TCR_ENABLE | TCR_PAD_EN)
// EPH Status Register
/* BANK 0 */
#define EPH_STATUS_REG SMC_REG(0x0002, 0)
#define ES_TX_SUC 0x0001 // Last TX was successful
#define ES_SNGL_COL 0x0002 // Single collision detected for last tx
#define ES_MUL_COL 0x0004 // Multiple collisions detected for last tx
#define ES_LTX_MULT 0x0008 // Last tx was a multicast
#define ES_16COL 0x0010 // 16 Collisions Reached
#define ES_SQET 0x0020 // Signal Quality Error Test
#define ES_LTXBRD 0x0040 // Last tx was a broadcast
#define ES_TXDEFR 0x0080 // Transmit Deferred
#define ES_LATCOL 0x0200 // Late collision detected on last tx
#define ES_LOSTCARR 0x0400 // Lost Carrier Sense
#define ES_EXC_DEF 0x0800 // Excessive Deferral
#define ES_CTR_ROL 0x1000 // Counter Roll Over indication
#define ES_LINK_OK 0x4000 // Driven by inverted value of nLNK pin
#define ES_TXUNRN 0x8000 // Tx Underrun
// Receive Control Register
/* BANK 0 */
#define RCR_REG SMC_REG(0x0004, 0)
#define RCR_RX_ABORT 0x0001 // Set if a rx frame was aborted
#define RCR_PRMS 0x0002 // Enable promiscuous mode
#define RCR_ALMUL 0x0004 // When set accepts all multicast frames
#define RCR_RXEN 0x0100 // IFF this is set, we can receive packets
#define RCR_STRIP_CRC 0x0200 // When set strips CRC from rx packets
#define RCR_ABORT_ENB 0x0200 // When set will abort rx on collision
#define RCR_FILT_CAR 0x0400 // When set filters leading 12 bit s of carrier
#define RCR_SOFTRST 0x8000 // resets the chip
/* the normal settings for the RCR register : */
#define RCR_DEFAULT (RCR_STRIP_CRC | RCR_RXEN)
#define RCR_CLEAR 0x0 // set it to a base state
// Counter Register
/* BANK 0 */
#define COUNTER_REG SMC_REG(0x0006, 0)
// Memory Information Register
/* BANK 0 */
#define MIR_REG SMC_REG(0x0008, 0)
// Receive/Phy Control Register
/* BANK 0 */
#define RPC_REG SMC_REG(0x000A, 0)
#define RPC_SPEED 0x2000 // When 1 PHY is in 100Mbps mode.
#define RPC_DPLX 0x1000 // When 1 PHY is in Full-Duplex Mode
#define RPC_ANEG 0x0800 // When 1 PHY is in Auto-Negotiate Mode
#define RPC_LSXA_SHFT 5 // Bits to shift LS2A,LS1A,LS0A to lsb
#define RPC_LSXB_SHFT 2 // Bits to get LS2B,LS1B,LS0B to lsb
#define RPC_LED_100_10 (0x00) // LED = 100Mbps OR's with 10Mbps link detect
#define RPC_LED_RES (0x01) // LED = Reserved
#define RPC_LED_10 (0x02) // LED = 10Mbps link detect
#define RPC_LED_FD (0x03) // LED = Full Duplex Mode
#define RPC_LED_TX_RX (0x04) // LED = TX or RX packet occurred
#define RPC_LED_100 (0x05) // LED = 100Mbps link dectect
#define RPC_LED_TX (0x06) // LED = TX packet occurred
#define RPC_LED_RX (0x07) // LED = RX packet occurred
#ifndef RPC_LSA_DEFAULT
#define RPC_LSA_DEFAULT RPC_LED_100
#endif
#ifndef RPC_LSB_DEFAULT
#define RPC_LSB_DEFAULT RPC_LED_FD
#endif
#define RPC_DEFAULT (RPC_ANEG | (RPC_LSA_DEFAULT << RPC_LSXA_SHFT) | (RPC_LSB_DEFAULT << RPC_LSXB_SHFT) | RPC_SPEED | RPC_DPLX)
/* Bank 0 0x0C is reserved */
// Bank Select Register
/* All Banks */
#define BSR_REG 0x000E
// Configuration Reg
/* BANK 1 */
#define CONFIG_REG SMC_REG(0x0000, 1)
#define CONFIG_EXT_PHY 0x0200 // 1=external MII, 0=internal Phy
#define CONFIG_GPCNTRL 0x0400 // Inverse value drives pin nCNTRL
#define CONFIG_NO_WAIT 0x1000 // When 1 no extra wait states on ISA bus
#define CONFIG_EPH_POWER_EN 0x8000 // When 0 EPH is placed into low power mode.
// Default is powered-up, Internal Phy, Wait States, and pin nCNTRL=low
#define CONFIG_DEFAULT (CONFIG_EPH_POWER_EN)
// Base Address Register
/* BANK 1 */
#define BASE_REG SMC_REG(0x0002, 1)
// Individual Address Registers
/* BANK 1 */
#define ADDR0_REG SMC_REG(0x0004, 1)
#define ADDR1_REG SMC_REG(0x0006, 1)
#define ADDR2_REG SMC_REG(0x0008, 1)
// General Purpose Register
/* BANK 1 */
#define GP_REG SMC_REG(0x000A, 1)
// Control Register
/* BANK 1 */
#define CTL_REG SMC_REG(0x000C, 1)
#define CTL_RCV_BAD 0x4000 // When 1 bad CRC packets are received
#define CTL_AUTO_RELEASE 0x0800 // When 1 tx pages are released automatically
#define CTL_LE_ENABLE 0x0080 // When 1 enables Link Error interrupt
#define CTL_CR_ENABLE 0x0040 // When 1 enables Counter Rollover interrupt
#define CTL_TE_ENABLE 0x0020 // When 1 enables Transmit Error interrupt
#define CTL_EEPROM_SELECT 0x0004 // Controls EEPROM reload & store
#define CTL_RELOAD 0x0002 // When set reads EEPROM into registers
#define CTL_STORE 0x0001 // When set stores registers into EEPROM
// MMU Command Register
/* BANK 2 */
#define MMU_CMD_REG SMC_REG(0x0000, 2)
#define MC_BUSY 1 // When 1 the last release has not completed
#define MC_NOP (0<<5) // No Op
#define MC_ALLOC (1<<5) // OR with number of 256 byte packets
#define MC_RESET (2<<5) // Reset MMU to initial state
#define MC_REMOVE (3<<5) // Remove the current rx packet
#define MC_RELEASE (4<<5) // Remove and release the current rx packet
#define MC_FREEPKT (5<<5) // Release packet in PNR register
#define MC_ENQUEUE (6<<5) // Enqueue the packet for transmit
#define MC_RSTTXFIFO (7<<5) // Reset the TX FIFOs
// Packet Number Register
/* BANK 2 */
#define PN_REG SMC_REG(0x0002, 2)
// Allocation Result Register
/* BANK 2 */
#define AR_REG SMC_REG(0x0003, 2)
#define AR_FAILED 0x80 // Alocation Failed
// TX FIFO Ports Register
/* BANK 2 */
#define TXFIFO_REG SMC_REG(0x0004, 2)
#define TXFIFO_TEMPTY 0x80 // TX FIFO Empty
// RX FIFO Ports Register
/* BANK 2 */
#define RXFIFO_REG SMC_REG(0x0005, 2)
#define RXFIFO_REMPTY 0x80 // RX FIFO Empty
#define FIFO_REG SMC_REG(0x0004, 2)
// Pointer Register
/* BANK 2 */
#define PTR_REG SMC_REG(0x0006, 2)
#define PTR_RCV 0x8000 // 1=Receive area, 0=Transmit area
#define PTR_AUTOINC 0x4000 // Auto increment the pointer on each access
#define PTR_READ 0x2000 // When 1 the operation is a read
// Data Register
/* BANK 2 */
#define DATA_REG SMC_REG(0x0008, 2)
// Interrupt Status/Acknowledge Register
/* BANK 2 */
#define INT_REG SMC_REG(0x000C, 2)
// Interrupt Mask Register
/* BANK 2 */
#define IM_REG SMC_REG(0x000D, 2)
#define IM_MDINT 0x80 // PHY MI Register 18 Interrupt
#define IM_ERCV_INT 0x40 // Early Receive Interrupt
#define IM_EPH_INT 0x20 // Set by Ethernet Protocol Handler section
#define IM_RX_OVRN_INT 0x10 // Set by Receiver Overruns
#define IM_ALLOC_INT 0x08 // Set when allocation request is completed
#define IM_TX_EMPTY_INT 0x04 // Set if the TX FIFO goes empty
#define IM_TX_INT 0x02 // Transmit Interrupt
#define IM_RCV_INT 0x01 // Receive Interrupt
// Multicast Table Registers
/* BANK 3 */
#define MCAST_REG1 SMC_REG(0x0000, 3)
#define MCAST_REG2 SMC_REG(0x0002, 3)
#define MCAST_REG3 SMC_REG(0x0004, 3)
#define MCAST_REG4 SMC_REG(0x0006, 3)
// Management Interface Register (MII)
/* BANK 3 */
#define MII_REG SMC_REG(0x0008, 3)
#define MII_MSK_CRS100 0x4000 // Disables CRS100 detection during tx half dup
#define MII_MDOE 0x0008 // MII Output Enable
#define MII_MCLK 0x0004 // MII Clock, pin MDCLK
#define MII_MDI 0x0002 // MII Input, pin MDI
#define MII_MDO 0x0001 // MII Output, pin MDO
// Revision Register
/* BANK 3 */
/* ( hi: chip id low: rev # ) */
#define REV_REG SMC_REG(0x000A, 3)
// Early RCV Register
/* BANK 3 */
/* this is NOT on SMC9192 */
#define ERCV_REG SMC_REG(0x000C, 3)
#define ERCV_RCV_DISCRD 0x0080 // When 1 discards a packet being received
#define ERCV_THRESHOLD 0x001F // ERCV Threshold Mask
// External Register
/* BANK 7 */
#define EXT_REG SMC_REG(0x0000, 7)
#define CHIP_9192 3
#define CHIP_9194 4
#define CHIP_9195 5
#define CHIP_9196 6
#define CHIP_91100 7
#define CHIP_91100FD 8
#define CHIP_91111FD 9
static
const
char
*
chip_ids
[
16
]
=
{
NULL
,
NULL
,
NULL
,
/* 3 */
"SMC91C90/91C92"
,
/* 4 */
"SMC91C94"
,
/* 5 */
"SMC91C95"
,
/* 6 */
"SMC91C96"
,
/* 7 */
"SMC91C100"
,
/* 8 */
"SMC91C100FD"
,
/* 9 */
"SMC91C11xFD"
,
NULL
,
NULL
,
NULL
,
NULL
,
NULL
,
NULL
};
/*
. Transmit status bits
*/
#define TS_SUCCESS 0x0001
#define TS_LOSTCAR 0x0400
#define TS_LATCOL 0x0200
#define TS_16COL 0x0010
/*
. Receive status bits
*/
#define RS_ALGNERR 0x8000
#define RS_BRODCAST 0x4000
#define RS_BADCRC 0x2000
#define RS_ODDFRAME 0x1000
#define RS_TOOLONG 0x0800
#define RS_TOOSHORT 0x0400
#define RS_MULTICAST 0x0001
#define RS_ERRORS (RS_ALGNERR | RS_BADCRC | RS_TOOLONG | RS_TOOSHORT)
/*
* PHY IDs
* LAN83C183 == LAN91C111 Internal PHY
*/
#define PHY_LAN83C183 0x0016f840
#define PHY_LAN83C180 0x02821c50
/*
* PHY Register Addresses (LAN91C111 Internal PHY)
*
* Generic PHY registers can be found in <linux/mii.h>
*
* These phy registers are specific to our on-board phy.
*/
// PHY Configuration Register 1
#define PHY_CFG1_REG 0x10
#define PHY_CFG1_LNKDIS 0x8000 // 1=Rx Link Detect Function disabled
#define PHY_CFG1_XMTDIS 0x4000 // 1=TP Transmitter Disabled
#define PHY_CFG1_XMTPDN 0x2000 // 1=TP Transmitter Powered Down
#define PHY_CFG1_BYPSCR 0x0400 // 1=Bypass scrambler/descrambler
#define PHY_CFG1_UNSCDS 0x0200 // 1=Unscramble Idle Reception Disable
#define PHY_CFG1_EQLZR 0x0100 // 1=Rx Equalizer Disabled
#define PHY_CFG1_CABLE 0x0080 // 1=STP(150ohm), 0=UTP(100ohm)
#define PHY_CFG1_RLVL0 0x0040 // 1=Rx Squelch level reduced by 4.5db
#define PHY_CFG1_TLVL_SHIFT 2 // Transmit Output Level Adjust
#define PHY_CFG1_TLVL_MASK 0x003C
#define PHY_CFG1_TRF_MASK 0x0003 // Transmitter Rise/Fall time
// PHY Configuration Register 2
#define PHY_CFG2_REG 0x11
#define PHY_CFG2_APOLDIS 0x0020 // 1=Auto Polarity Correction disabled
#define PHY_CFG2_JABDIS 0x0010 // 1=Jabber disabled
#define PHY_CFG2_MREG 0x0008 // 1=Multiple register access (MII mgt)
#define PHY_CFG2_INTMDIO 0x0004 // 1=Interrupt signaled with MDIO pulseo
// PHY Status Output (and Interrupt status) Register
#define PHY_INT_REG 0x12 // Status Output (Interrupt Status)
#define PHY_INT_INT 0x8000 // 1=bits have changed since last read
#define PHY_INT_LNKFAIL 0x4000 // 1=Link Not detected
#define PHY_INT_LOSSSYNC 0x2000 // 1=Descrambler has lost sync
#define PHY_INT_CWRD 0x1000 // 1=Invalid 4B5B code detected on rx
#define PHY_INT_SSD 0x0800 // 1=No Start Of Stream detected on rx
#define PHY_INT_ESD 0x0400 // 1=No End Of Stream detected on rx
#define PHY_INT_RPOL 0x0200 // 1=Reverse Polarity detected
#define PHY_INT_JAB 0x0100 // 1=Jabber detected
#define PHY_INT_SPDDET 0x0080 // 1=100Base-TX mode, 0=10Base-T mode
#define PHY_INT_DPLXDET 0x0040 // 1=Device in Full Duplex
// PHY Interrupt/Status Mask Register
#define PHY_MASK_REG 0x13 // Interrupt Mask
// Uses the same bit definitions as PHY_INT_REG
/*
* SMC91C96 ethernet config and status registers.
* These are in the "attribute" space.
*/
#define ECOR 0x8000
#define ECOR_RESET 0x80
#define ECOR_LEVEL_IRQ 0x40
#define ECOR_WR_ATTRIB 0x04
#define ECOR_ENABLE 0x01
#define ECSR 0x8002
#define ECSR_IOIS8 0x20
#define ECSR_PWRDWN 0x04
#define ECSR_INT 0x02
#define ATTRIB_SIZE ((64*1024) << SMC_IO_SHIFT)
/*
* Macros to abstract register access according to the data bus
* capabilities. Please use those and not the in/out primitives.
* Note: the following macros do *not* select the bank -- this must
* be done separately as needed in the main code. The SMC_REG() macro
* only uses the bank argument for debugging purposes (when enabled).
*/
#if SMC_DEBUG > 0
#define SMC_REG(reg, bank) \
({ \
int __b = SMC_CURRENT_BANK(); \
if (unlikely((__b & ~0xf0) != (0x3300 | bank))) { \
printk( "%s: bank reg screwed (0x%04x)\n", \
CARDNAME, __b ); \
BUG(); \
} \
reg<<SMC_IO_SHIFT; \
})
#else
#define SMC_REG(reg, bank) (reg<<SMC_IO_SHIFT)
#endif
#if SMC_CAN_USE_8BIT
#define SMC_GET_PN() SMC_inb( ioaddr, PN_REG )
#define SMC_SET_PN(x) SMC_outb( x, ioaddr, PN_REG )
#define SMC_GET_AR() SMC_inb( ioaddr, AR_REG )
#define SMC_GET_TXFIFO() SMC_inb( ioaddr, TXFIFO_REG )
#define SMC_GET_RXFIFO() SMC_inb( ioaddr, RXFIFO_REG )
#define SMC_GET_INT() SMC_inb( ioaddr, INT_REG )
#define SMC_ACK_INT(x) SMC_outb( x, ioaddr, INT_REG )
#define SMC_GET_INT_MASK() SMC_inb( ioaddr, IM_REG )
#define SMC_SET_INT_MASK(x) SMC_outb( x, ioaddr, IM_REG )
#else
#define SMC_GET_PN() (SMC_inw( ioaddr, PN_REG ) & 0xFF)
#define SMC_SET_PN(x) SMC_outw( x, ioaddr, PN_REG )
#define SMC_GET_AR() (SMC_inw( ioaddr, PN_REG ) >> 8)
#define SMC_GET_TXFIFO() (SMC_inw( ioaddr, TXFIFO_REG ) & 0xFF)
#define SMC_GET_RXFIFO() (SMC_inw( ioaddr, TXFIFO_REG ) >> 8)
#define SMC_GET_INT() (SMC_inw( ioaddr, INT_REG ) & 0xFF)
#define SMC_ACK_INT(x) \
do { \
unsigned long __flags; \
int __mask; \
local_irq_save(__flags); \
__mask = SMC_inw( ioaddr, INT_REG ) & ~0xff; \
SMC_outw( __mask | (x), ioaddr, INT_REG ); \
local_irq_restore(__flags); \
} while (0)
#define SMC_GET_INT_MASK() (SMC_inw( ioaddr, INT_REG ) >> 8)
#define SMC_SET_INT_MASK(x) SMC_outw( (x) << 8, ioaddr, INT_REG )
#endif
#define SMC_CURRENT_BANK() SMC_inw( ioaddr, BANK_SELECT )
#define SMC_SELECT_BANK(x) SMC_outw( x, ioaddr, BANK_SELECT )
#define SMC_GET_BASE() SMC_inw( ioaddr, BASE_REG )
#define SMC_SET_BASE(x) SMC_outw( x, ioaddr, BASE_REG )
#define SMC_GET_CONFIG() SMC_inw( ioaddr, CONFIG_REG )
#define SMC_SET_CONFIG(x) SMC_outw( x, ioaddr, CONFIG_REG )
#define SMC_GET_COUNTER() SMC_inw( ioaddr, COUNTER_REG )
#define SMC_GET_CTL() SMC_inw( ioaddr, CTL_REG )
#define SMC_SET_CTL(x) SMC_outw( x, ioaddr, CTL_REG )
#define SMC_GET_MII() SMC_inw( ioaddr, MII_REG )
#define SMC_SET_MII(x) SMC_outw( x, ioaddr, MII_REG )
#define SMC_GET_MIR() SMC_inw( ioaddr, MIR_REG )
#define SMC_SET_MIR(x) SMC_outw( x, ioaddr, MIR_REG )
#define SMC_GET_MMU_CMD() SMC_inw( ioaddr, MMU_CMD_REG )
#define SMC_SET_MMU_CMD(x) SMC_outw( x, ioaddr, MMU_CMD_REG )
#define SMC_GET_FIFO() SMC_inw( ioaddr, FIFO_REG )
#define SMC_GET_PTR() SMC_inw( ioaddr, PTR_REG )
#define SMC_SET_PTR(x) SMC_outw( x, ioaddr, PTR_REG )
#define SMC_GET_RCR() SMC_inw( ioaddr, RCR_REG )
#define SMC_SET_RCR(x) SMC_outw( x, ioaddr, RCR_REG )
#define SMC_GET_REV() SMC_inw( ioaddr, REV_REG )
#define SMC_GET_RPC() SMC_inw( ioaddr, RPC_REG )
#define SMC_SET_RPC(x) SMC_outw( x, ioaddr, RPC_REG )
#define SMC_GET_TCR() SMC_inw( ioaddr, TCR_REG )
#define SMC_SET_TCR(x) SMC_outw( x, ioaddr, TCR_REG )
#ifndef SMC_GET_MAC_ADDR
#define SMC_GET_MAC_ADDR(addr) \
do { \
unsigned int __v; \
__v = SMC_inw( ioaddr, ADDR0_REG ); \
addr[0] = __v; addr[1] = __v >> 8; \
__v = SMC_inw( ioaddr, ADDR1_REG ); \
addr[2] = __v; addr[3] = __v >> 8; \
__v = SMC_inw( ioaddr, ADDR2_REG ); \
addr[4] = __v; addr[5] = __v >> 8; \
} while (0)
#endif
#define SMC_SET_MAC_ADDR(addr) \
do { \
SMC_outw( addr[0]|(addr[1] << 8), ioaddr, ADDR0_REG ); \
SMC_outw( addr[2]|(addr[3] << 8), ioaddr, ADDR1_REG ); \
SMC_outw( addr[4]|(addr[5] << 8), ioaddr, ADDR2_REG ); \
} while (0)
#define SMC_CLEAR_MCAST() \
do { \
SMC_outw( 0, ioaddr, MCAST_REG1 ); \
SMC_outw( 0, ioaddr, MCAST_REG2 ); \
SMC_outw( 0, ioaddr, MCAST_REG3 ); \
SMC_outw( 0, ioaddr, MCAST_REG4 ); \
} while (0)
#define SMC_SET_MCAST(x) \
do { \
unsigned char *mt = (x); \
SMC_outw( mt[0] | (mt[1] << 8), ioaddr, MCAST_REG1 ); \
SMC_outw( mt[2] | (mt[3] << 8), ioaddr, MCAST_REG2 ); \
SMC_outw( mt[4] | (mt[5] << 8), ioaddr, MCAST_REG3 ); \
SMC_outw( mt[6] | (mt[7] << 8), ioaddr, MCAST_REG4 ); \
} while (0)
#if SMC_CAN_USE_32BIT
/*
* Some setups just can't write 8 or 16 bits reliably when not aligned
* to a 32 bit boundary. I tell you that exists!
* We re-do the ones here that can be easily worked around if they can have
* their low parts written to 0 without adverse effects.
*/
#undef SMC_SELECT_BANK
#define SMC_SELECT_BANK(x) SMC_outl( (x)<<16, ioaddr, 12<<SMC_IO_SHIFT )
#undef SMC_SET_RPC
#define SMC_SET_RPC(x) SMC_outl( (x)<<16, ioaddr, SMC_REG(8, 0) )
#undef SMC_SET_PN
#define SMC_SET_PN(x) SMC_outl( (x)<<16, ioaddr, SMC_REG(0, 2) )
#undef SMC_SET_PTR
#define SMC_SET_PTR(x) SMC_outl( (x)<<16, ioaddr, SMC_REG(4, 2) )
#endif
#if SMC_CAN_USE_32BIT
#define SMC_PUT_PKT_HDR(status, length) \
SMC_outl( (status) | (length) << 16, ioaddr, DATA_REG )
#define SMC_GET_PKT_HDR(status, length) \
do { \
unsigned int __val = SMC_inl( ioaddr, DATA_REG ); \
(status) = __val & 0xffff; \
(length) = __val >> 16; \
} while (0)
#else
#define SMC_PUT_PKT_HDR(status, length) \
do { \
SMC_outw( status, ioaddr, DATA_REG ); \
SMC_outw( length, ioaddr, DATA_REG ); \
} while (0)
#define SMC_GET_PKT_HDR(status, length) \
do { \
(status) = SMC_inw( ioaddr, DATA_REG ); \
(length) = SMC_inw( ioaddr, DATA_REG ); \
} while (0)
#endif
#if SMC_CAN_USE_32BIT
#define SMC_PUSH_DATA(p, l) \
do { \
char *__ptr = (p); \
int __len = (l); \
if (__len >= 2 && (long)__ptr & 2) { \
__len -= 2; \
SMC_outw( *((u16 *)__ptr)++, ioaddr, DATA_REG );\
} \
SMC_outsl( ioaddr, DATA_REG, __ptr, __len >> 2); \
if (__len & 2) { \
__ptr += (__len & ~3); \
SMC_outw( *((u16 *)__ptr), ioaddr, DATA_REG ); \
} \
} while (0)
#define SMC_PULL_DATA(p, l) \
do { \
char *__ptr = (p); \
int __len = (l); \
if ((long)__ptr & 2) { \
/* \
* We want 32bit alignment here. \
* Since some buses perform a full 32bit \
* fetch even for 16bit data we can't use \
* SMC_inw() here. Back both source (on chip \
* and destination) pointers of 2 bytes. \
*/
\
(long)__ptr &= ~2; \
__len += 2; \
SMC_SET_PTR( 2|PTR_READ|PTR_RCV|PTR_AUTOINC ); \
} \
__len += 2; \
SMC_insl( ioaddr, DATA_REG, __ptr, __len >> 2); \
} while (0)
#elif SMC_CAN_USE_16BIT
#define SMC_PUSH_DATA(p, l) SMC_outsw( ioaddr, DATA_REG, p, (l) >> 1 )
#define SMC_PULL_DATA(p, l) SMC_insw ( ioaddr, DATA_REG, p, (l) >> 1 )
#elif SMC_CAN_USE_8BIT
#define SMC_PUSH_DATA(p, l) SMC_outsb( ioaddr, DATA_REG, p, l )
#define SMC_PULL_DATA(p, l) SMC_insb ( ioaddr, DATA_REG, p, l )
#endif
#if ! SMC_CAN_USE_16BIT
#define SMC_outw(x, ioaddr, reg) \
do { \
unsigned int __val16 = (x); \
SMC_outb( __val16, ioaddr, reg ); \
SMC_outb( __val16 >> 8, ioaddr, reg + (1 << SMC_IO_SHIFT));\
} while (0)
#define SMC_inw(ioaddr, reg) \
({ \
unsigned int __val16; \
__val16 = SMC_inb( ioaddr, reg ); \
__val16 |= SMC_inb( ioaddr, reg + (1 << SMC_IO_SHIFT)) << 8; \
__val16; \
})
#endif
#endif
/* _SMC91X_H_ */
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