mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2024-11-24 12:44:11 +08:00
New driver "sfc" for Solarstorm SFC4000 controller.
The driver supports the 10Xpress PHY and XFP modules on our reference designs SFE4001 and SFE4002 and the SMC models SMC10GPCIe-XFP and SMC10GPCIe-10BT. Signed-off-by: Ben Hutchings <bhutchings@solarflare.com> Signed-off-by: Jeff Garzik <jgarzik@redhat.com>
This commit is contained in:
parent
358c12953b
commit
8ceee660aa
@ -3522,6 +3522,13 @@ M: pfg@sgi.com
|
||||
L: linux-ia64@vger.kernel.org
|
||||
S: Supported
|
||||
|
||||
SFC NETWORK DRIVER
|
||||
P: Steve Hodgson
|
||||
P: Ben Hutchings
|
||||
P: Robert Stonehouse
|
||||
M: linux-net-drivers@solarflare.com
|
||||
S: Supported
|
||||
|
||||
SGI VISUAL WORKSTATION 320 AND 540
|
||||
P: Andrey Panin
|
||||
M: pazke@donpac.ru
|
||||
|
@ -2592,6 +2592,7 @@ config BNX2X
|
||||
To compile this driver as a module, choose M here: the module
|
||||
will be called bnx2x. This is recommended.
|
||||
|
||||
source "drivers/net/sfc/Kconfig"
|
||||
|
||||
endif # NETDEV_10000
|
||||
|
||||
|
@ -252,3 +252,5 @@ obj-$(CONFIG_FS_ENET) += fs_enet/
|
||||
obj-$(CONFIG_NETXEN_NIC) += netxen/
|
||||
obj-$(CONFIG_NIU) += niu.o
|
||||
obj-$(CONFIG_VIRTIO_NET) += virtio_net.o
|
||||
obj-$(CONFIG_SFC) += sfc/
|
||||
|
||||
|
12
drivers/net/sfc/Kconfig
Normal file
12
drivers/net/sfc/Kconfig
Normal file
@ -0,0 +1,12 @@
|
||||
config SFC
|
||||
tristate "Solarflare Solarstorm SFC4000 support"
|
||||
depends on PCI && INET
|
||||
select MII
|
||||
select INET_LRO
|
||||
select CRC32
|
||||
help
|
||||
This driver supports 10-gigabit Ethernet cards based on
|
||||
the Solarflare Communications Solarstorm SFC4000 controller.
|
||||
|
||||
To compile this driver as a module, choose M here. The module
|
||||
will be called sfc.
|
5
drivers/net/sfc/Makefile
Normal file
5
drivers/net/sfc/Makefile
Normal file
@ -0,0 +1,5 @@
|
||||
sfc-y += efx.o falcon.o tx.o rx.o falcon_xmac.o \
|
||||
i2c-direct.o ethtool.o xfp_phy.o mdio_10g.o \
|
||||
tenxpress.o boards.o sfe4001.o
|
||||
|
||||
obj-$(CONFIG_SFC) += sfc.o
|
508
drivers/net/sfc/bitfield.h
Normal file
508
drivers/net/sfc/bitfield.h
Normal file
@ -0,0 +1,508 @@
|
||||
/****************************************************************************
|
||||
* Driver for Solarflare Solarstorm network controllers and boards
|
||||
* Copyright 2005-2006 Fen Systems Ltd.
|
||||
* Copyright 2006-2008 Solarflare Communications Inc.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 as published
|
||||
* by the Free Software Foundation, incorporated herein by reference.
|
||||
*/
|
||||
|
||||
#ifndef EFX_BITFIELD_H
|
||||
#define EFX_BITFIELD_H
|
||||
|
||||
/*
|
||||
* Efx bitfield access
|
||||
*
|
||||
* Efx NICs make extensive use of bitfields up to 128 bits
|
||||
* wide. Since there is no native 128-bit datatype on most systems,
|
||||
* and since 64-bit datatypes are inefficient on 32-bit systems and
|
||||
* vice versa, we wrap accesses in a way that uses the most efficient
|
||||
* datatype.
|
||||
*
|
||||
* The NICs are PCI devices and therefore little-endian. Since most
|
||||
* of the quantities that we deal with are DMAed to/from host memory,
|
||||
* we define our datatypes (efx_oword_t, efx_qword_t and
|
||||
* efx_dword_t) to be little-endian.
|
||||
*/
|
||||
|
||||
/* Lowest bit numbers and widths */
|
||||
#define EFX_DUMMY_FIELD_LBN 0
|
||||
#define EFX_DUMMY_FIELD_WIDTH 0
|
||||
#define EFX_DWORD_0_LBN 0
|
||||
#define EFX_DWORD_0_WIDTH 32
|
||||
#define EFX_DWORD_1_LBN 32
|
||||
#define EFX_DWORD_1_WIDTH 32
|
||||
#define EFX_DWORD_2_LBN 64
|
||||
#define EFX_DWORD_2_WIDTH 32
|
||||
#define EFX_DWORD_3_LBN 96
|
||||
#define EFX_DWORD_3_WIDTH 32
|
||||
|
||||
/* Specified attribute (e.g. LBN) of the specified field */
|
||||
#define EFX_VAL(field, attribute) field ## _ ## attribute
|
||||
/* Low bit number of the specified field */
|
||||
#define EFX_LOW_BIT(field) EFX_VAL(field, LBN)
|
||||
/* Bit width of the specified field */
|
||||
#define EFX_WIDTH(field) EFX_VAL(field, WIDTH)
|
||||
/* High bit number of the specified field */
|
||||
#define EFX_HIGH_BIT(field) (EFX_LOW_BIT(field) + EFX_WIDTH(field) - 1)
|
||||
/* Mask equal in width to the specified field.
|
||||
*
|
||||
* For example, a field with width 5 would have a mask of 0x1f.
|
||||
*
|
||||
* The maximum width mask that can be generated is 64 bits.
|
||||
*/
|
||||
#define EFX_MASK64(field) \
|
||||
(EFX_WIDTH(field) == 64 ? ~((u64) 0) : \
|
||||
(((((u64) 1) << EFX_WIDTH(field))) - 1))
|
||||
|
||||
/* Mask equal in width to the specified field.
|
||||
*
|
||||
* For example, a field with width 5 would have a mask of 0x1f.
|
||||
*
|
||||
* The maximum width mask that can be generated is 32 bits. Use
|
||||
* EFX_MASK64 for higher width fields.
|
||||
*/
|
||||
#define EFX_MASK32(field) \
|
||||
(EFX_WIDTH(field) == 32 ? ~((u32) 0) : \
|
||||
(((((u32) 1) << EFX_WIDTH(field))) - 1))
|
||||
|
||||
/* A doubleword (i.e. 4 byte) datatype - little-endian in HW */
|
||||
typedef union efx_dword {
|
||||
__le32 u32[1];
|
||||
} efx_dword_t;
|
||||
|
||||
/* A quadword (i.e. 8 byte) datatype - little-endian in HW */
|
||||
typedef union efx_qword {
|
||||
__le64 u64[1];
|
||||
__le32 u32[2];
|
||||
efx_dword_t dword[2];
|
||||
} efx_qword_t;
|
||||
|
||||
/* An octword (eight-word, i.e. 16 byte) datatype - little-endian in HW */
|
||||
typedef union efx_oword {
|
||||
__le64 u64[2];
|
||||
efx_qword_t qword[2];
|
||||
__le32 u32[4];
|
||||
efx_dword_t dword[4];
|
||||
} efx_oword_t;
|
||||
|
||||
/* Format string and value expanders for printk */
|
||||
#define EFX_DWORD_FMT "%08x"
|
||||
#define EFX_QWORD_FMT "%08x:%08x"
|
||||
#define EFX_OWORD_FMT "%08x:%08x:%08x:%08x"
|
||||
#define EFX_DWORD_VAL(dword) \
|
||||
((unsigned int) le32_to_cpu((dword).u32[0]))
|
||||
#define EFX_QWORD_VAL(qword) \
|
||||
((unsigned int) le32_to_cpu((qword).u32[1])), \
|
||||
((unsigned int) le32_to_cpu((qword).u32[0]))
|
||||
#define EFX_OWORD_VAL(oword) \
|
||||
((unsigned int) le32_to_cpu((oword).u32[3])), \
|
||||
((unsigned int) le32_to_cpu((oword).u32[2])), \
|
||||
((unsigned int) le32_to_cpu((oword).u32[1])), \
|
||||
((unsigned int) le32_to_cpu((oword).u32[0]))
|
||||
|
||||
/*
|
||||
* Extract bit field portion [low,high) from the native-endian element
|
||||
* which contains bits [min,max).
|
||||
*
|
||||
* For example, suppose "element" represents the high 32 bits of a
|
||||
* 64-bit value, and we wish to extract the bits belonging to the bit
|
||||
* field occupying bits 28-45 of this 64-bit value.
|
||||
*
|
||||
* Then EFX_EXTRACT ( element, 32, 63, 28, 45 ) would give
|
||||
*
|
||||
* ( element ) << 4
|
||||
*
|
||||
* The result will contain the relevant bits filled in in the range
|
||||
* [0,high-low), with garbage in bits [high-low+1,...).
|
||||
*/
|
||||
#define EFX_EXTRACT_NATIVE(native_element, min, max, low, high) \
|
||||
(((low > max) || (high < min)) ? 0 : \
|
||||
((low > min) ? \
|
||||
((native_element) >> (low - min)) : \
|
||||
((native_element) << (min - low))))
|
||||
|
||||
/*
|
||||
* Extract bit field portion [low,high) from the 64-bit little-endian
|
||||
* element which contains bits [min,max)
|
||||
*/
|
||||
#define EFX_EXTRACT64(element, min, max, low, high) \
|
||||
EFX_EXTRACT_NATIVE(le64_to_cpu(element), min, max, low, high)
|
||||
|
||||
/*
|
||||
* Extract bit field portion [low,high) from the 32-bit little-endian
|
||||
* element which contains bits [min,max)
|
||||
*/
|
||||
#define EFX_EXTRACT32(element, min, max, low, high) \
|
||||
EFX_EXTRACT_NATIVE(le32_to_cpu(element), min, max, low, high)
|
||||
|
||||
#define EFX_EXTRACT_OWORD64(oword, low, high) \
|
||||
(EFX_EXTRACT64((oword).u64[0], 0, 63, low, high) | \
|
||||
EFX_EXTRACT64((oword).u64[1], 64, 127, low, high))
|
||||
|
||||
#define EFX_EXTRACT_QWORD64(qword, low, high) \
|
||||
EFX_EXTRACT64((qword).u64[0], 0, 63, low, high)
|
||||
|
||||
#define EFX_EXTRACT_OWORD32(oword, low, high) \
|
||||
(EFX_EXTRACT32((oword).u32[0], 0, 31, low, high) | \
|
||||
EFX_EXTRACT32((oword).u32[1], 32, 63, low, high) | \
|
||||
EFX_EXTRACT32((oword).u32[2], 64, 95, low, high) | \
|
||||
EFX_EXTRACT32((oword).u32[3], 96, 127, low, high))
|
||||
|
||||
#define EFX_EXTRACT_QWORD32(qword, low, high) \
|
||||
(EFX_EXTRACT32((qword).u32[0], 0, 31, low, high) | \
|
||||
EFX_EXTRACT32((qword).u32[1], 32, 63, low, high))
|
||||
|
||||
#define EFX_EXTRACT_DWORD(dword, low, high) \
|
||||
EFX_EXTRACT32((dword).u32[0], 0, 31, low, high)
|
||||
|
||||
#define EFX_OWORD_FIELD64(oword, field) \
|
||||
(EFX_EXTRACT_OWORD64(oword, EFX_LOW_BIT(field), EFX_HIGH_BIT(field)) \
|
||||
& EFX_MASK64(field))
|
||||
|
||||
#define EFX_QWORD_FIELD64(qword, field) \
|
||||
(EFX_EXTRACT_QWORD64(qword, EFX_LOW_BIT(field), EFX_HIGH_BIT(field)) \
|
||||
& EFX_MASK64(field))
|
||||
|
||||
#define EFX_OWORD_FIELD32(oword, field) \
|
||||
(EFX_EXTRACT_OWORD32(oword, EFX_LOW_BIT(field), EFX_HIGH_BIT(field)) \
|
||||
& EFX_MASK32(field))
|
||||
|
||||
#define EFX_QWORD_FIELD32(qword, field) \
|
||||
(EFX_EXTRACT_QWORD32(qword, EFX_LOW_BIT(field), EFX_HIGH_BIT(field)) \
|
||||
& EFX_MASK32(field))
|
||||
|
||||
#define EFX_DWORD_FIELD(dword, field) \
|
||||
(EFX_EXTRACT_DWORD(dword, EFX_LOW_BIT(field), EFX_HIGH_BIT(field)) \
|
||||
& EFX_MASK32(field))
|
||||
|
||||
#define EFX_OWORD_IS_ZERO64(oword) \
|
||||
(((oword).u64[0] | (oword).u64[1]) == (__force __le64) 0)
|
||||
|
||||
#define EFX_QWORD_IS_ZERO64(qword) \
|
||||
(((qword).u64[0]) == (__force __le64) 0)
|
||||
|
||||
#define EFX_OWORD_IS_ZERO32(oword) \
|
||||
(((oword).u32[0] | (oword).u32[1] | (oword).u32[2] | (oword).u32[3]) \
|
||||
== (__force __le32) 0)
|
||||
|
||||
#define EFX_QWORD_IS_ZERO32(qword) \
|
||||
(((qword).u32[0] | (qword).u32[1]) == (__force __le32) 0)
|
||||
|
||||
#define EFX_DWORD_IS_ZERO(dword) \
|
||||
(((dword).u32[0]) == (__force __le32) 0)
|
||||
|
||||
#define EFX_OWORD_IS_ALL_ONES64(oword) \
|
||||
(((oword).u64[0] & (oword).u64[1]) == ~((__force __le64) 0))
|
||||
|
||||
#define EFX_QWORD_IS_ALL_ONES64(qword) \
|
||||
((qword).u64[0] == ~((__force __le64) 0))
|
||||
|
||||
#define EFX_OWORD_IS_ALL_ONES32(oword) \
|
||||
(((oword).u32[0] & (oword).u32[1] & (oword).u32[2] & (oword).u32[3]) \
|
||||
== ~((__force __le32) 0))
|
||||
|
||||
#define EFX_QWORD_IS_ALL_ONES32(qword) \
|
||||
(((qword).u32[0] & (qword).u32[1]) == ~((__force __le32) 0))
|
||||
|
||||
#define EFX_DWORD_IS_ALL_ONES(dword) \
|
||||
((dword).u32[0] == ~((__force __le32) 0))
|
||||
|
||||
#if BITS_PER_LONG == 64
|
||||
#define EFX_OWORD_FIELD EFX_OWORD_FIELD64
|
||||
#define EFX_QWORD_FIELD EFX_QWORD_FIELD64
|
||||
#define EFX_OWORD_IS_ZERO EFX_OWORD_IS_ZERO64
|
||||
#define EFX_QWORD_IS_ZERO EFX_QWORD_IS_ZERO64
|
||||
#define EFX_OWORD_IS_ALL_ONES EFX_OWORD_IS_ALL_ONES64
|
||||
#define EFX_QWORD_IS_ALL_ONES EFX_QWORD_IS_ALL_ONES64
|
||||
#else
|
||||
#define EFX_OWORD_FIELD EFX_OWORD_FIELD32
|
||||
#define EFX_QWORD_FIELD EFX_QWORD_FIELD32
|
||||
#define EFX_OWORD_IS_ZERO EFX_OWORD_IS_ZERO32
|
||||
#define EFX_QWORD_IS_ZERO EFX_QWORD_IS_ZERO32
|
||||
#define EFX_OWORD_IS_ALL_ONES EFX_OWORD_IS_ALL_ONES32
|
||||
#define EFX_QWORD_IS_ALL_ONES EFX_QWORD_IS_ALL_ONES32
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Construct bit field portion
|
||||
*
|
||||
* Creates the portion of the bit field [low,high) that lies within
|
||||
* the range [min,max).
|
||||
*/
|
||||
#define EFX_INSERT_NATIVE64(min, max, low, high, value) \
|
||||
(((low > max) || (high < min)) ? 0 : \
|
||||
((low > min) ? \
|
||||
(((u64) (value)) << (low - min)) : \
|
||||
(((u64) (value)) >> (min - low))))
|
||||
|
||||
#define EFX_INSERT_NATIVE32(min, max, low, high, value) \
|
||||
(((low > max) || (high < min)) ? 0 : \
|
||||
((low > min) ? \
|
||||
(((u32) (value)) << (low - min)) : \
|
||||
(((u32) (value)) >> (min - low))))
|
||||
|
||||
#define EFX_INSERT_NATIVE(min, max, low, high, value) \
|
||||
((((max - min) >= 32) || ((high - low) >= 32)) ? \
|
||||
EFX_INSERT_NATIVE64(min, max, low, high, value) : \
|
||||
EFX_INSERT_NATIVE32(min, max, low, high, value))
|
||||
|
||||
/*
|
||||
* Construct bit field portion
|
||||
*
|
||||
* Creates the portion of the named bit field that lies within the
|
||||
* range [min,max).
|
||||
*/
|
||||
#define EFX_INSERT_FIELD_NATIVE(min, max, field, value) \
|
||||
EFX_INSERT_NATIVE(min, max, EFX_LOW_BIT(field), \
|
||||
EFX_HIGH_BIT(field), value)
|
||||
|
||||
/*
|
||||
* Construct bit field
|
||||
*
|
||||
* Creates the portion of the named bit fields that lie within the
|
||||
* range [min,max).
|
||||
*/
|
||||
#define EFX_INSERT_FIELDS_NATIVE(min, max, \
|
||||
field1, value1, \
|
||||
field2, value2, \
|
||||
field3, value3, \
|
||||
field4, value4, \
|
||||
field5, value5, \
|
||||
field6, value6, \
|
||||
field7, value7, \
|
||||
field8, value8, \
|
||||
field9, value9, \
|
||||
field10, value10) \
|
||||
(EFX_INSERT_FIELD_NATIVE((min), (max), field1, (value1)) | \
|
||||
EFX_INSERT_FIELD_NATIVE((min), (max), field2, (value2)) | \
|
||||
EFX_INSERT_FIELD_NATIVE((min), (max), field3, (value3)) | \
|
||||
EFX_INSERT_FIELD_NATIVE((min), (max), field4, (value4)) | \
|
||||
EFX_INSERT_FIELD_NATIVE((min), (max), field5, (value5)) | \
|
||||
EFX_INSERT_FIELD_NATIVE((min), (max), field6, (value6)) | \
|
||||
EFX_INSERT_FIELD_NATIVE((min), (max), field7, (value7)) | \
|
||||
EFX_INSERT_FIELD_NATIVE((min), (max), field8, (value8)) | \
|
||||
EFX_INSERT_FIELD_NATIVE((min), (max), field9, (value9)) | \
|
||||
EFX_INSERT_FIELD_NATIVE((min), (max), field10, (value10)))
|
||||
|
||||
#define EFX_INSERT_FIELDS64(...) \
|
||||
cpu_to_le64(EFX_INSERT_FIELDS_NATIVE(__VA_ARGS__))
|
||||
|
||||
#define EFX_INSERT_FIELDS32(...) \
|
||||
cpu_to_le32(EFX_INSERT_FIELDS_NATIVE(__VA_ARGS__))
|
||||
|
||||
#define EFX_POPULATE_OWORD64(oword, ...) do { \
|
||||
(oword).u64[0] = EFX_INSERT_FIELDS64(0, 63, __VA_ARGS__); \
|
||||
(oword).u64[1] = EFX_INSERT_FIELDS64(64, 127, __VA_ARGS__); \
|
||||
} while (0)
|
||||
|
||||
#define EFX_POPULATE_QWORD64(qword, ...) do { \
|
||||
(qword).u64[0] = EFX_INSERT_FIELDS64(0, 63, __VA_ARGS__); \
|
||||
} while (0)
|
||||
|
||||
#define EFX_POPULATE_OWORD32(oword, ...) do { \
|
||||
(oword).u32[0] = EFX_INSERT_FIELDS32(0, 31, __VA_ARGS__); \
|
||||
(oword).u32[1] = EFX_INSERT_FIELDS32(32, 63, __VA_ARGS__); \
|
||||
(oword).u32[2] = EFX_INSERT_FIELDS32(64, 95, __VA_ARGS__); \
|
||||
(oword).u32[3] = EFX_INSERT_FIELDS32(96, 127, __VA_ARGS__); \
|
||||
} while (0)
|
||||
|
||||
#define EFX_POPULATE_QWORD32(qword, ...) do { \
|
||||
(qword).u32[0] = EFX_INSERT_FIELDS32(0, 31, __VA_ARGS__); \
|
||||
(qword).u32[1] = EFX_INSERT_FIELDS32(32, 63, __VA_ARGS__); \
|
||||
} while (0)
|
||||
|
||||
#define EFX_POPULATE_DWORD(dword, ...) do { \
|
||||
(dword).u32[0] = EFX_INSERT_FIELDS32(0, 31, __VA_ARGS__); \
|
||||
} while (0)
|
||||
|
||||
#if BITS_PER_LONG == 64
|
||||
#define EFX_POPULATE_OWORD EFX_POPULATE_OWORD64
|
||||
#define EFX_POPULATE_QWORD EFX_POPULATE_QWORD64
|
||||
#else
|
||||
#define EFX_POPULATE_OWORD EFX_POPULATE_OWORD32
|
||||
#define EFX_POPULATE_QWORD EFX_POPULATE_QWORD32
|
||||
#endif
|
||||
|
||||
/* Populate an octword field with various numbers of arguments */
|
||||
#define EFX_POPULATE_OWORD_10 EFX_POPULATE_OWORD
|
||||
#define EFX_POPULATE_OWORD_9(oword, ...) \
|
||||
EFX_POPULATE_OWORD_10(oword, EFX_DUMMY_FIELD, 0, __VA_ARGS__)
|
||||
#define EFX_POPULATE_OWORD_8(oword, ...) \
|
||||
EFX_POPULATE_OWORD_9(oword, EFX_DUMMY_FIELD, 0, __VA_ARGS__)
|
||||
#define EFX_POPULATE_OWORD_7(oword, ...) \
|
||||
EFX_POPULATE_OWORD_8(oword, EFX_DUMMY_FIELD, 0, __VA_ARGS__)
|
||||
#define EFX_POPULATE_OWORD_6(oword, ...) \
|
||||
EFX_POPULATE_OWORD_7(oword, EFX_DUMMY_FIELD, 0, __VA_ARGS__)
|
||||
#define EFX_POPULATE_OWORD_5(oword, ...) \
|
||||
EFX_POPULATE_OWORD_6(oword, EFX_DUMMY_FIELD, 0, __VA_ARGS__)
|
||||
#define EFX_POPULATE_OWORD_4(oword, ...) \
|
||||
EFX_POPULATE_OWORD_5(oword, EFX_DUMMY_FIELD, 0, __VA_ARGS__)
|
||||
#define EFX_POPULATE_OWORD_3(oword, ...) \
|
||||
EFX_POPULATE_OWORD_4(oword, EFX_DUMMY_FIELD, 0, __VA_ARGS__)
|
||||
#define EFX_POPULATE_OWORD_2(oword, ...) \
|
||||
EFX_POPULATE_OWORD_3(oword, EFX_DUMMY_FIELD, 0, __VA_ARGS__)
|
||||
#define EFX_POPULATE_OWORD_1(oword, ...) \
|
||||
EFX_POPULATE_OWORD_2(oword, EFX_DUMMY_FIELD, 0, __VA_ARGS__)
|
||||
#define EFX_ZERO_OWORD(oword) \
|
||||
EFX_POPULATE_OWORD_1(oword, EFX_DUMMY_FIELD, 0)
|
||||
#define EFX_SET_OWORD(oword) \
|
||||
EFX_POPULATE_OWORD_4(oword, \
|
||||
EFX_DWORD_0, 0xffffffff, \
|
||||
EFX_DWORD_1, 0xffffffff, \
|
||||
EFX_DWORD_2, 0xffffffff, \
|
||||
EFX_DWORD_3, 0xffffffff)
|
||||
|
||||
/* Populate a quadword field with various numbers of arguments */
|
||||
#define EFX_POPULATE_QWORD_10 EFX_POPULATE_QWORD
|
||||
#define EFX_POPULATE_QWORD_9(qword, ...) \
|
||||
EFX_POPULATE_QWORD_10(qword, EFX_DUMMY_FIELD, 0, __VA_ARGS__)
|
||||
#define EFX_POPULATE_QWORD_8(qword, ...) \
|
||||
EFX_POPULATE_QWORD_9(qword, EFX_DUMMY_FIELD, 0, __VA_ARGS__)
|
||||
#define EFX_POPULATE_QWORD_7(qword, ...) \
|
||||
EFX_POPULATE_QWORD_8(qword, EFX_DUMMY_FIELD, 0, __VA_ARGS__)
|
||||
#define EFX_POPULATE_QWORD_6(qword, ...) \
|
||||
EFX_POPULATE_QWORD_7(qword, EFX_DUMMY_FIELD, 0, __VA_ARGS__)
|
||||
#define EFX_POPULATE_QWORD_5(qword, ...) \
|
||||
EFX_POPULATE_QWORD_6(qword, EFX_DUMMY_FIELD, 0, __VA_ARGS__)
|
||||
#define EFX_POPULATE_QWORD_4(qword, ...) \
|
||||
EFX_POPULATE_QWORD_5(qword, EFX_DUMMY_FIELD, 0, __VA_ARGS__)
|
||||
#define EFX_POPULATE_QWORD_3(qword, ...) \
|
||||
EFX_POPULATE_QWORD_4(qword, EFX_DUMMY_FIELD, 0, __VA_ARGS__)
|
||||
#define EFX_POPULATE_QWORD_2(qword, ...) \
|
||||
EFX_POPULATE_QWORD_3(qword, EFX_DUMMY_FIELD, 0, __VA_ARGS__)
|
||||
#define EFX_POPULATE_QWORD_1(qword, ...) \
|
||||
EFX_POPULATE_QWORD_2(qword, EFX_DUMMY_FIELD, 0, __VA_ARGS__)
|
||||
#define EFX_ZERO_QWORD(qword) \
|
||||
EFX_POPULATE_QWORD_1(qword, EFX_DUMMY_FIELD, 0)
|
||||
#define EFX_SET_QWORD(qword) \
|
||||
EFX_POPULATE_QWORD_2(qword, \
|
||||
EFX_DWORD_0, 0xffffffff, \
|
||||
EFX_DWORD_1, 0xffffffff)
|
||||
|
||||
/* Populate a dword field with various numbers of arguments */
|
||||
#define EFX_POPULATE_DWORD_10 EFX_POPULATE_DWORD
|
||||
#define EFX_POPULATE_DWORD_9(dword, ...) \
|
||||
EFX_POPULATE_DWORD_10(dword, EFX_DUMMY_FIELD, 0, __VA_ARGS__)
|
||||
#define EFX_POPULATE_DWORD_8(dword, ...) \
|
||||
EFX_POPULATE_DWORD_9(dword, EFX_DUMMY_FIELD, 0, __VA_ARGS__)
|
||||
#define EFX_POPULATE_DWORD_7(dword, ...) \
|
||||
EFX_POPULATE_DWORD_8(dword, EFX_DUMMY_FIELD, 0, __VA_ARGS__)
|
||||
#define EFX_POPULATE_DWORD_6(dword, ...) \
|
||||
EFX_POPULATE_DWORD_7(dword, EFX_DUMMY_FIELD, 0, __VA_ARGS__)
|
||||
#define EFX_POPULATE_DWORD_5(dword, ...) \
|
||||
EFX_POPULATE_DWORD_6(dword, EFX_DUMMY_FIELD, 0, __VA_ARGS__)
|
||||
#define EFX_POPULATE_DWORD_4(dword, ...) \
|
||||
EFX_POPULATE_DWORD_5(dword, EFX_DUMMY_FIELD, 0, __VA_ARGS__)
|
||||
#define EFX_POPULATE_DWORD_3(dword, ...) \
|
||||
EFX_POPULATE_DWORD_4(dword, EFX_DUMMY_FIELD, 0, __VA_ARGS__)
|
||||
#define EFX_POPULATE_DWORD_2(dword, ...) \
|
||||
EFX_POPULATE_DWORD_3(dword, EFX_DUMMY_FIELD, 0, __VA_ARGS__)
|
||||
#define EFX_POPULATE_DWORD_1(dword, ...) \
|
||||
EFX_POPULATE_DWORD_2(dword, EFX_DUMMY_FIELD, 0, __VA_ARGS__)
|
||||
#define EFX_ZERO_DWORD(dword) \
|
||||
EFX_POPULATE_DWORD_1(dword, EFX_DUMMY_FIELD, 0)
|
||||
#define EFX_SET_DWORD(dword) \
|
||||
EFX_POPULATE_DWORD_1(dword, EFX_DWORD_0, 0xffffffff)
|
||||
|
||||
/*
|
||||
* Modify a named field within an already-populated structure. Used
|
||||
* for read-modify-write operations.
|
||||
*
|
||||
*/
|
||||
|
||||
#define EFX_INVERT_OWORD(oword) do { \
|
||||
(oword).u64[0] = ~((oword).u64[0]); \
|
||||
(oword).u64[1] = ~((oword).u64[1]); \
|
||||
} while (0)
|
||||
|
||||
#define EFX_INSERT_FIELD64(...) \
|
||||
cpu_to_le64(EFX_INSERT_FIELD_NATIVE(__VA_ARGS__))
|
||||
|
||||
#define EFX_INSERT_FIELD32(...) \
|
||||
cpu_to_le32(EFX_INSERT_FIELD_NATIVE(__VA_ARGS__))
|
||||
|
||||
#define EFX_INPLACE_MASK64(min, max, field) \
|
||||
EFX_INSERT_FIELD64(min, max, field, EFX_MASK64(field))
|
||||
|
||||
#define EFX_INPLACE_MASK32(min, max, field) \
|
||||
EFX_INSERT_FIELD32(min, max, field, EFX_MASK32(field))
|
||||
|
||||
#define EFX_SET_OWORD_FIELD64(oword, field, value) do { \
|
||||
(oword).u64[0] = (((oword).u64[0] \
|
||||
& ~EFX_INPLACE_MASK64(0, 63, field)) \
|
||||
| EFX_INSERT_FIELD64(0, 63, field, value)); \
|
||||
(oword).u64[1] = (((oword).u64[1] \
|
||||
& ~EFX_INPLACE_MASK64(64, 127, field)) \
|
||||
| EFX_INSERT_FIELD64(64, 127, field, value)); \
|
||||
} while (0)
|
||||
|
||||
#define EFX_SET_QWORD_FIELD64(qword, field, value) do { \
|
||||
(qword).u64[0] = (((qword).u64[0] \
|
||||
& ~EFX_INPLACE_MASK64(0, 63, field)) \
|
||||
| EFX_INSERT_FIELD64(0, 63, field, value)); \
|
||||
} while (0)
|
||||
|
||||
#define EFX_SET_OWORD_FIELD32(oword, field, value) do { \
|
||||
(oword).u32[0] = (((oword).u32[0] \
|
||||
& ~EFX_INPLACE_MASK32(0, 31, field)) \
|
||||
| EFX_INSERT_FIELD32(0, 31, field, value)); \
|
||||
(oword).u32[1] = (((oword).u32[1] \
|
||||
& ~EFX_INPLACE_MASK32(32, 63, field)) \
|
||||
| EFX_INSERT_FIELD32(32, 63, field, value)); \
|
||||
(oword).u32[2] = (((oword).u32[2] \
|
||||
& ~EFX_INPLACE_MASK32(64, 95, field)) \
|
||||
| EFX_INSERT_FIELD32(64, 95, field, value)); \
|
||||
(oword).u32[3] = (((oword).u32[3] \
|
||||
& ~EFX_INPLACE_MASK32(96, 127, field)) \
|
||||
| EFX_INSERT_FIELD32(96, 127, field, value)); \
|
||||
} while (0)
|
||||
|
||||
#define EFX_SET_QWORD_FIELD32(qword, field, value) do { \
|
||||
(qword).u32[0] = (((qword).u32[0] \
|
||||
& ~EFX_INPLACE_MASK32(0, 31, field)) \
|
||||
| EFX_INSERT_FIELD32(0, 31, field, value)); \
|
||||
(qword).u32[1] = (((qword).u32[1] \
|
||||
& ~EFX_INPLACE_MASK32(32, 63, field)) \
|
||||
| EFX_INSERT_FIELD32(32, 63, field, value)); \
|
||||
} while (0)
|
||||
|
||||
#define EFX_SET_DWORD_FIELD(dword, field, value) do { \
|
||||
(dword).u32[0] = (((dword).u32[0] \
|
||||
& ~EFX_INPLACE_MASK32(0, 31, field)) \
|
||||
| EFX_INSERT_FIELD32(0, 31, field, value)); \
|
||||
} while (0)
|
||||
|
||||
#if BITS_PER_LONG == 64
|
||||
#define EFX_SET_OWORD_FIELD EFX_SET_OWORD_FIELD64
|
||||
#define EFX_SET_QWORD_FIELD EFX_SET_QWORD_FIELD64
|
||||
#else
|
||||
#define EFX_SET_OWORD_FIELD EFX_SET_OWORD_FIELD32
|
||||
#define EFX_SET_QWORD_FIELD EFX_SET_QWORD_FIELD32
|
||||
#endif
|
||||
|
||||
#define EFX_SET_OWORD_FIELD_VER(efx, oword, field, value) do { \
|
||||
if (FALCON_REV(efx) >= FALCON_REV_B0) { \
|
||||
EFX_SET_OWORD_FIELD((oword), field##_B0, (value)); \
|
||||
} else { \
|
||||
EFX_SET_OWORD_FIELD((oword), field##_A1, (value)); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define EFX_QWORD_FIELD_VER(efx, qword, field) \
|
||||
(FALCON_REV(efx) >= FALCON_REV_B0 ? \
|
||||
EFX_QWORD_FIELD((qword), field##_B0) : \
|
||||
EFX_QWORD_FIELD((qword), field##_A1))
|
||||
|
||||
/* Used to avoid compiler warnings about shift range exceeding width
|
||||
* of the data types when dma_addr_t is only 32 bits wide.
|
||||
*/
|
||||
#define DMA_ADDR_T_WIDTH (8 * sizeof(dma_addr_t))
|
||||
#define EFX_DMA_TYPE_WIDTH(width) \
|
||||
(((width) < DMA_ADDR_T_WIDTH) ? (width) : DMA_ADDR_T_WIDTH)
|
||||
#define EFX_DMA_MAX_MASK ((DMA_ADDR_T_WIDTH == 64) ? \
|
||||
~((u64) 0) : ~((u32) 0))
|
||||
#define EFX_DMA_MASK(mask) ((mask) & EFX_DMA_MAX_MASK)
|
||||
|
||||
#endif /* EFX_BITFIELD_H */
|
167
drivers/net/sfc/boards.c
Normal file
167
drivers/net/sfc/boards.c
Normal file
@ -0,0 +1,167 @@
|
||||
/****************************************************************************
|
||||
* Driver for Solarflare Solarstorm network controllers and boards
|
||||
* Copyright 2007 Solarflare Communications Inc.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 as published
|
||||
* by the Free Software Foundation, incorporated herein by reference.
|
||||
*/
|
||||
|
||||
#include "net_driver.h"
|
||||
#include "phy.h"
|
||||
#include "boards.h"
|
||||
#include "efx.h"
|
||||
|
||||
/* Macros for unpacking the board revision */
|
||||
/* The revision info is in host byte order. */
|
||||
#define BOARD_TYPE(_rev) (_rev >> 8)
|
||||
#define BOARD_MAJOR(_rev) ((_rev >> 4) & 0xf)
|
||||
#define BOARD_MINOR(_rev) (_rev & 0xf)
|
||||
|
||||
/* Blink support. If the PHY has no auto-blink mode so we hang it off a timer */
|
||||
#define BLINK_INTERVAL (HZ/2)
|
||||
|
||||
static void blink_led_timer(unsigned long context)
|
||||
{
|
||||
struct efx_nic *efx = (struct efx_nic *)context;
|
||||
struct efx_blinker *bl = &efx->board_info.blinker;
|
||||
efx->board_info.set_fault_led(efx, bl->state);
|
||||
bl->state = !bl->state;
|
||||
if (bl->resubmit) {
|
||||
bl->timer.expires = jiffies + BLINK_INTERVAL;
|
||||
add_timer(&bl->timer);
|
||||
}
|
||||
}
|
||||
|
||||
static void board_blink(struct efx_nic *efx, int blink)
|
||||
{
|
||||
struct efx_blinker *blinker = &efx->board_info.blinker;
|
||||
|
||||
/* The rtnl mutex serialises all ethtool ioctls, so
|
||||
* nothing special needs doing here. */
|
||||
if (blink) {
|
||||
blinker->resubmit = 1;
|
||||
blinker->state = 0;
|
||||
setup_timer(&blinker->timer, blink_led_timer,
|
||||
(unsigned long)efx);
|
||||
blinker->timer.expires = jiffies + BLINK_INTERVAL;
|
||||
add_timer(&blinker->timer);
|
||||
} else {
|
||||
blinker->resubmit = 0;
|
||||
if (blinker->timer.function)
|
||||
del_timer_sync(&blinker->timer);
|
||||
efx->board_info.set_fault_led(efx, 0);
|
||||
}
|
||||
}
|
||||
|
||||
/*****************************************************************************
|
||||
* Support for the SFE4002
|
||||
*
|
||||
*/
|
||||
/****************************************************************************/
|
||||
/* LED allocations. Note that on rev A0 boards the schematic and the reality
|
||||
* differ: red and green are swapped. Below is the fixed (A1) layout (there
|
||||
* are only 3 A0 boards in existence, so no real reason to make this
|
||||
* conditional).
|
||||
*/
|
||||
#define SFE4002_FAULT_LED (2) /* Red */
|
||||
#define SFE4002_RX_LED (0) /* Green */
|
||||
#define SFE4002_TX_LED (1) /* Amber */
|
||||
|
||||
static int sfe4002_init_leds(struct efx_nic *efx)
|
||||
{
|
||||
/* Set the TX and RX LEDs to reflect status and activity, and the
|
||||
* fault LED off */
|
||||
xfp_set_led(efx, SFE4002_TX_LED,
|
||||
QUAKE_LED_TXLINK | QUAKE_LED_LINK_ACTSTAT);
|
||||
xfp_set_led(efx, SFE4002_RX_LED,
|
||||
QUAKE_LED_RXLINK | QUAKE_LED_LINK_ACTSTAT);
|
||||
xfp_set_led(efx, SFE4002_FAULT_LED, QUAKE_LED_OFF);
|
||||
efx->board_info.blinker.led_num = SFE4002_FAULT_LED;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void sfe4002_fault_led(struct efx_nic *efx, int state)
|
||||
{
|
||||
xfp_set_led(efx, SFE4002_FAULT_LED, state ? QUAKE_LED_ON :
|
||||
QUAKE_LED_OFF);
|
||||
}
|
||||
|
||||
static int sfe4002_init(struct efx_nic *efx)
|
||||
{
|
||||
efx->board_info.init_leds = sfe4002_init_leds;
|
||||
efx->board_info.set_fault_led = sfe4002_fault_led;
|
||||
efx->board_info.blink = board_blink;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* This will get expanded as board-specific details get moved out of the
|
||||
* PHY drivers. */
|
||||
struct efx_board_data {
|
||||
const char *ref_model;
|
||||
const char *gen_type;
|
||||
int (*init) (struct efx_nic *nic);
|
||||
};
|
||||
|
||||
static int dummy_init(struct efx_nic *nic)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct efx_board_data board_data[] = {
|
||||
[EFX_BOARD_INVALID] =
|
||||
{NULL, NULL, dummy_init},
|
||||
[EFX_BOARD_SFE4001] =
|
||||
{"SFE4001", "10GBASE-T adapter", sfe4001_poweron},
|
||||
[EFX_BOARD_SFE4002] =
|
||||
{"SFE4002", "XFP adapter", sfe4002_init},
|
||||
};
|
||||
|
||||
int efx_set_board_info(struct efx_nic *efx, u16 revision_info)
|
||||
{
|
||||
int rc = 0;
|
||||
struct efx_board_data *data;
|
||||
|
||||
if (BOARD_TYPE(revision_info) >= EFX_BOARD_MAX) {
|
||||
EFX_ERR(efx, "squashing unknown board type %d\n",
|
||||
BOARD_TYPE(revision_info));
|
||||
revision_info = 0;
|
||||
}
|
||||
|
||||
if (BOARD_TYPE(revision_info) == 0) {
|
||||
efx->board_info.major = 0;
|
||||
efx->board_info.minor = 0;
|
||||
/* For early boards that don't have revision info. there is
|
||||
* only 1 board for each PHY type, so we can work it out, with
|
||||
* the exception of the PHY-less boards. */
|
||||
switch (efx->phy_type) {
|
||||
case PHY_TYPE_10XPRESS:
|
||||
efx->board_info.type = EFX_BOARD_SFE4001;
|
||||
break;
|
||||
case PHY_TYPE_XFP:
|
||||
efx->board_info.type = EFX_BOARD_SFE4002;
|
||||
break;
|
||||
default:
|
||||
efx->board_info.type = 0;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
efx->board_info.type = BOARD_TYPE(revision_info);
|
||||
efx->board_info.major = BOARD_MAJOR(revision_info);
|
||||
efx->board_info.minor = BOARD_MINOR(revision_info);
|
||||
}
|
||||
|
||||
data = &board_data[efx->board_info.type];
|
||||
|
||||
/* Report the board model number or generic type for recognisable
|
||||
* boards. */
|
||||
if (efx->board_info.type != 0)
|
||||
EFX_INFO(efx, "board is %s rev %c%d\n",
|
||||
(efx->pci_dev->subsystem_vendor == EFX_VENDID_SFC)
|
||||
? data->ref_model : data->gen_type,
|
||||
'A' + efx->board_info.major, efx->board_info.minor);
|
||||
|
||||
efx->board_info.init = data->init;
|
||||
|
||||
return rc;
|
||||
}
|
26
drivers/net/sfc/boards.h
Normal file
26
drivers/net/sfc/boards.h
Normal file
@ -0,0 +1,26 @@
|
||||
/****************************************************************************
|
||||
* Driver for Solarflare Solarstorm network controllers and boards
|
||||
* Copyright 2007 Solarflare Communications Inc.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 as published
|
||||
* by the Free Software Foundation, incorporated herein by reference.
|
||||
*/
|
||||
|
||||
#ifndef EFX_BOARDS_H
|
||||
#define EFX_BOARDS_H
|
||||
|
||||
/* Board IDs (must fit in 8 bits) */
|
||||
enum efx_board_type {
|
||||
EFX_BOARD_INVALID = 0,
|
||||
EFX_BOARD_SFE4001 = 1, /* SFE4001 (10GBASE-T) */
|
||||
EFX_BOARD_SFE4002 = 2,
|
||||
/* Insert new types before here */
|
||||
EFX_BOARD_MAX
|
||||
};
|
||||
|
||||
extern int efx_set_board_info(struct efx_nic *efx, u16 revision_info);
|
||||
extern int sfe4001_poweron(struct efx_nic *efx);
|
||||
extern void sfe4001_poweroff(struct efx_nic *efx);
|
||||
|
||||
#endif
|
2208
drivers/net/sfc/efx.c
Normal file
2208
drivers/net/sfc/efx.c
Normal file
File diff suppressed because it is too large
Load Diff
67
drivers/net/sfc/efx.h
Normal file
67
drivers/net/sfc/efx.h
Normal file
@ -0,0 +1,67 @@
|
||||
/****************************************************************************
|
||||
* Driver for Solarflare Solarstorm network controllers and boards
|
||||
* Copyright 2005-2006 Fen Systems Ltd.
|
||||
* Copyright 2006-2008 Solarflare Communications Inc.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 as published
|
||||
* by the Free Software Foundation, incorporated herein by reference.
|
||||
*/
|
||||
|
||||
#ifndef EFX_EFX_H
|
||||
#define EFX_EFX_H
|
||||
|
||||
#include "net_driver.h"
|
||||
|
||||
/* PCI IDs */
|
||||
#define EFX_VENDID_SFC 0x1924
|
||||
#define FALCON_A_P_DEVID 0x0703
|
||||
#define FALCON_A_S_DEVID 0x6703
|
||||
#define FALCON_B_P_DEVID 0x0710
|
||||
|
||||
/* TX */
|
||||
extern int efx_xmit(struct efx_nic *efx,
|
||||
struct efx_tx_queue *tx_queue, struct sk_buff *skb);
|
||||
extern void efx_stop_queue(struct efx_nic *efx);
|
||||
extern void efx_wake_queue(struct efx_nic *efx);
|
||||
|
||||
/* RX */
|
||||
extern void efx_xmit_done(struct efx_tx_queue *tx_queue, unsigned int index);
|
||||
extern void efx_rx_packet(struct efx_rx_queue *rx_queue, unsigned int index,
|
||||
unsigned int len, int checksummed, int discard);
|
||||
extern void efx_schedule_slow_fill(struct efx_rx_queue *rx_queue, int delay);
|
||||
|
||||
/* Channels */
|
||||
extern void efx_process_channel_now(struct efx_channel *channel);
|
||||
extern int efx_flush_queues(struct efx_nic *efx);
|
||||
|
||||
/* Ports */
|
||||
extern void efx_reconfigure_port(struct efx_nic *efx);
|
||||
|
||||
/* Global */
|
||||
extern void efx_schedule_reset(struct efx_nic *efx, enum reset_type type);
|
||||
extern void efx_suspend(struct efx_nic *efx);
|
||||
extern void efx_resume(struct efx_nic *efx);
|
||||
extern void efx_init_irq_moderation(struct efx_nic *efx, int tx_usecs,
|
||||
int rx_usecs);
|
||||
extern int efx_request_power(struct efx_nic *efx, int mw, const char *name);
|
||||
extern void efx_hex_dump(const u8 *, unsigned int, const char *);
|
||||
|
||||
/* Dummy PHY ops for PHY drivers */
|
||||
extern int efx_port_dummy_op_int(struct efx_nic *efx);
|
||||
extern void efx_port_dummy_op_void(struct efx_nic *efx);
|
||||
extern void efx_port_dummy_op_blink(struct efx_nic *efx, int blink);
|
||||
|
||||
|
||||
extern unsigned int efx_monitor_interval;
|
||||
|
||||
static inline void efx_schedule_channel(struct efx_channel *channel)
|
||||
{
|
||||
EFX_TRACE(channel->efx, "channel %d scheduling NAPI poll on CPU%d\n",
|
||||
channel->channel, raw_smp_processor_id());
|
||||
channel->work_pending = 1;
|
||||
|
||||
netif_rx_schedule(channel->napi_dev, &channel->napi_str);
|
||||
}
|
||||
|
||||
#endif /* EFX_EFX_H */
|
50
drivers/net/sfc/enum.h
Normal file
50
drivers/net/sfc/enum.h
Normal file
@ -0,0 +1,50 @@
|
||||
/****************************************************************************
|
||||
* Driver for Solarflare Solarstorm network controllers and boards
|
||||
* Copyright 2007 Solarflare Communications Inc.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 as published
|
||||
* by the Free Software Foundation, incorporated herein by reference.
|
||||
*/
|
||||
|
||||
#ifndef EFX_ENUM_H
|
||||
#define EFX_ENUM_H
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
/**
|
||||
* enum reset_type - reset types
|
||||
*
|
||||
* %RESET_TYPE_INVSIBLE, %RESET_TYPE_ALL, %RESET_TYPE_WORLD and
|
||||
* %RESET_TYPE_DISABLE specify the method/scope of the reset. The
|
||||
* other valuesspecify reasons, which efx_schedule_reset() will choose
|
||||
* a method for.
|
||||
*
|
||||
* @RESET_TYPE_INVISIBLE: don't reset the PHYs or interrupts
|
||||
* @RESET_TYPE_ALL: reset everything but PCI core blocks
|
||||
* @RESET_TYPE_WORLD: reset everything, save & restore PCI config
|
||||
* @RESET_TYPE_DISABLE: disable NIC
|
||||
* @RESET_TYPE_MONITOR: reset due to hardware monitor
|
||||
* @RESET_TYPE_INT_ERROR: reset due to internal error
|
||||
* @RESET_TYPE_RX_RECOVERY: reset to recover from RX datapath errors
|
||||
* @RESET_TYPE_RX_DESC_FETCH: pcie error during rx descriptor fetch
|
||||
* @RESET_TYPE_TX_DESC_FETCH: pcie error during tx descriptor fetch
|
||||
* @RESET_TYPE_TX_SKIP: hardware completed empty tx descriptors
|
||||
*/
|
||||
enum reset_type {
|
||||
RESET_TYPE_NONE = -1,
|
||||
RESET_TYPE_INVISIBLE = 0,
|
||||
RESET_TYPE_ALL = 1,
|
||||
RESET_TYPE_WORLD = 2,
|
||||
RESET_TYPE_DISABLE = 3,
|
||||
RESET_TYPE_MAX_METHOD,
|
||||
RESET_TYPE_MONITOR,
|
||||
RESET_TYPE_INT_ERROR,
|
||||
RESET_TYPE_RX_RECOVERY,
|
||||
RESET_TYPE_RX_DESC_FETCH,
|
||||
RESET_TYPE_TX_DESC_FETCH,
|
||||
RESET_TYPE_TX_SKIP,
|
||||
RESET_TYPE_MAX,
|
||||
};
|
||||
|
||||
#endif /* EFX_ENUM_H */
|
460
drivers/net/sfc/ethtool.c
Normal file
460
drivers/net/sfc/ethtool.c
Normal file
@ -0,0 +1,460 @@
|
||||
/****************************************************************************
|
||||
* Driver for Solarflare Solarstorm network controllers and boards
|
||||
* Copyright 2005-2006 Fen Systems Ltd.
|
||||
* Copyright 2006-2008 Solarflare Communications Inc.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 as published
|
||||
* by the Free Software Foundation, incorporated herein by reference.
|
||||
*/
|
||||
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/ethtool.h>
|
||||
#include <linux/rtnetlink.h>
|
||||
#include "net_driver.h"
|
||||
#include "efx.h"
|
||||
#include "ethtool.h"
|
||||
#include "falcon.h"
|
||||
#include "gmii.h"
|
||||
#include "mac.h"
|
||||
|
||||
static int efx_ethtool_set_tx_csum(struct net_device *net_dev, u32 enable);
|
||||
|
||||
struct ethtool_string {
|
||||
char name[ETH_GSTRING_LEN];
|
||||
};
|
||||
|
||||
struct efx_ethtool_stat {
|
||||
const char *name;
|
||||
enum {
|
||||
EFX_ETHTOOL_STAT_SOURCE_mac_stats,
|
||||
EFX_ETHTOOL_STAT_SOURCE_nic,
|
||||
EFX_ETHTOOL_STAT_SOURCE_channel
|
||||
} source;
|
||||
unsigned offset;
|
||||
u64(*get_stat) (void *field); /* Reader function */
|
||||
};
|
||||
|
||||
/* Initialiser for a struct #efx_ethtool_stat with type-checking */
|
||||
#define EFX_ETHTOOL_STAT(stat_name, source_name, field, field_type, \
|
||||
get_stat_function) { \
|
||||
.name = #stat_name, \
|
||||
.source = EFX_ETHTOOL_STAT_SOURCE_##source_name, \
|
||||
.offset = ((((field_type *) 0) == \
|
||||
&((struct efx_##source_name *)0)->field) ? \
|
||||
offsetof(struct efx_##source_name, field) : \
|
||||
offsetof(struct efx_##source_name, field)), \
|
||||
.get_stat = get_stat_function, \
|
||||
}
|
||||
|
||||
static u64 efx_get_uint_stat(void *field)
|
||||
{
|
||||
return *(unsigned int *)field;
|
||||
}
|
||||
|
||||
static u64 efx_get_ulong_stat(void *field)
|
||||
{
|
||||
return *(unsigned long *)field;
|
||||
}
|
||||
|
||||
static u64 efx_get_u64_stat(void *field)
|
||||
{
|
||||
return *(u64 *) field;
|
||||
}
|
||||
|
||||
static u64 efx_get_atomic_stat(void *field)
|
||||
{
|
||||
return atomic_read((atomic_t *) field);
|
||||
}
|
||||
|
||||
#define EFX_ETHTOOL_ULONG_MAC_STAT(field) \
|
||||
EFX_ETHTOOL_STAT(field, mac_stats, field, \
|
||||
unsigned long, efx_get_ulong_stat)
|
||||
|
||||
#define EFX_ETHTOOL_U64_MAC_STAT(field) \
|
||||
EFX_ETHTOOL_STAT(field, mac_stats, field, \
|
||||
u64, efx_get_u64_stat)
|
||||
|
||||
#define EFX_ETHTOOL_UINT_NIC_STAT(name) \
|
||||
EFX_ETHTOOL_STAT(name, nic, n_##name, \
|
||||
unsigned int, efx_get_uint_stat)
|
||||
|
||||
#define EFX_ETHTOOL_ATOMIC_NIC_ERROR_STAT(field) \
|
||||
EFX_ETHTOOL_STAT(field, nic, field, \
|
||||
atomic_t, efx_get_atomic_stat)
|
||||
|
||||
#define EFX_ETHTOOL_UINT_CHANNEL_STAT(field) \
|
||||
EFX_ETHTOOL_STAT(field, channel, n_##field, \
|
||||
unsigned int, efx_get_uint_stat)
|
||||
|
||||
static struct efx_ethtool_stat efx_ethtool_stats[] = {
|
||||
EFX_ETHTOOL_U64_MAC_STAT(tx_bytes),
|
||||
EFX_ETHTOOL_U64_MAC_STAT(tx_good_bytes),
|
||||
EFX_ETHTOOL_U64_MAC_STAT(tx_bad_bytes),
|
||||
EFX_ETHTOOL_ULONG_MAC_STAT(tx_packets),
|
||||
EFX_ETHTOOL_ULONG_MAC_STAT(tx_bad),
|
||||
EFX_ETHTOOL_ULONG_MAC_STAT(tx_pause),
|
||||
EFX_ETHTOOL_ULONG_MAC_STAT(tx_control),
|
||||
EFX_ETHTOOL_ULONG_MAC_STAT(tx_unicast),
|
||||
EFX_ETHTOOL_ULONG_MAC_STAT(tx_multicast),
|
||||
EFX_ETHTOOL_ULONG_MAC_STAT(tx_broadcast),
|
||||
EFX_ETHTOOL_ULONG_MAC_STAT(tx_lt64),
|
||||
EFX_ETHTOOL_ULONG_MAC_STAT(tx_64),
|
||||
EFX_ETHTOOL_ULONG_MAC_STAT(tx_65_to_127),
|
||||
EFX_ETHTOOL_ULONG_MAC_STAT(tx_128_to_255),
|
||||
EFX_ETHTOOL_ULONG_MAC_STAT(tx_256_to_511),
|
||||
EFX_ETHTOOL_ULONG_MAC_STAT(tx_512_to_1023),
|
||||
EFX_ETHTOOL_ULONG_MAC_STAT(tx_1024_to_15xx),
|
||||
EFX_ETHTOOL_ULONG_MAC_STAT(tx_15xx_to_jumbo),
|
||||
EFX_ETHTOOL_ULONG_MAC_STAT(tx_gtjumbo),
|
||||
EFX_ETHTOOL_ULONG_MAC_STAT(tx_collision),
|
||||
EFX_ETHTOOL_ULONG_MAC_STAT(tx_single_collision),
|
||||
EFX_ETHTOOL_ULONG_MAC_STAT(tx_multiple_collision),
|
||||
EFX_ETHTOOL_ULONG_MAC_STAT(tx_excessive_collision),
|
||||
EFX_ETHTOOL_ULONG_MAC_STAT(tx_deferred),
|
||||
EFX_ETHTOOL_ULONG_MAC_STAT(tx_late_collision),
|
||||
EFX_ETHTOOL_ULONG_MAC_STAT(tx_excessive_deferred),
|
||||
EFX_ETHTOOL_ULONG_MAC_STAT(tx_non_tcpudp),
|
||||
EFX_ETHTOOL_ULONG_MAC_STAT(tx_mac_src_error),
|
||||
EFX_ETHTOOL_ULONG_MAC_STAT(tx_ip_src_error),
|
||||
EFX_ETHTOOL_U64_MAC_STAT(rx_bytes),
|
||||
EFX_ETHTOOL_U64_MAC_STAT(rx_good_bytes),
|
||||
EFX_ETHTOOL_U64_MAC_STAT(rx_bad_bytes),
|
||||
EFX_ETHTOOL_ULONG_MAC_STAT(rx_packets),
|
||||
EFX_ETHTOOL_ULONG_MAC_STAT(rx_good),
|
||||
EFX_ETHTOOL_ULONG_MAC_STAT(rx_bad),
|
||||
EFX_ETHTOOL_ULONG_MAC_STAT(rx_pause),
|
||||
EFX_ETHTOOL_ULONG_MAC_STAT(rx_control),
|
||||
EFX_ETHTOOL_ULONG_MAC_STAT(rx_unicast),
|
||||
EFX_ETHTOOL_ULONG_MAC_STAT(rx_multicast),
|
||||
EFX_ETHTOOL_ULONG_MAC_STAT(rx_broadcast),
|
||||
EFX_ETHTOOL_ULONG_MAC_STAT(rx_lt64),
|
||||
EFX_ETHTOOL_ULONG_MAC_STAT(rx_64),
|
||||
EFX_ETHTOOL_ULONG_MAC_STAT(rx_65_to_127),
|
||||
EFX_ETHTOOL_ULONG_MAC_STAT(rx_128_to_255),
|
||||
EFX_ETHTOOL_ULONG_MAC_STAT(rx_256_to_511),
|
||||
EFX_ETHTOOL_ULONG_MAC_STAT(rx_512_to_1023),
|
||||
EFX_ETHTOOL_ULONG_MAC_STAT(rx_1024_to_15xx),
|
||||
EFX_ETHTOOL_ULONG_MAC_STAT(rx_15xx_to_jumbo),
|
||||
EFX_ETHTOOL_ULONG_MAC_STAT(rx_gtjumbo),
|
||||
EFX_ETHTOOL_ULONG_MAC_STAT(rx_bad_lt64),
|
||||
EFX_ETHTOOL_ULONG_MAC_STAT(rx_bad_64_to_15xx),
|
||||
EFX_ETHTOOL_ULONG_MAC_STAT(rx_bad_15xx_to_jumbo),
|
||||
EFX_ETHTOOL_ULONG_MAC_STAT(rx_bad_gtjumbo),
|
||||
EFX_ETHTOOL_ULONG_MAC_STAT(rx_overflow),
|
||||
EFX_ETHTOOL_ULONG_MAC_STAT(rx_missed),
|
||||
EFX_ETHTOOL_ULONG_MAC_STAT(rx_false_carrier),
|
||||
EFX_ETHTOOL_ULONG_MAC_STAT(rx_symbol_error),
|
||||
EFX_ETHTOOL_ULONG_MAC_STAT(rx_align_error),
|
||||
EFX_ETHTOOL_ULONG_MAC_STAT(rx_length_error),
|
||||
EFX_ETHTOOL_ULONG_MAC_STAT(rx_internal_error),
|
||||
EFX_ETHTOOL_UINT_NIC_STAT(rx_nodesc_drop_cnt),
|
||||
EFX_ETHTOOL_ATOMIC_NIC_ERROR_STAT(rx_reset),
|
||||
EFX_ETHTOOL_UINT_CHANNEL_STAT(rx_tobe_disc),
|
||||
EFX_ETHTOOL_UINT_CHANNEL_STAT(rx_ip_hdr_chksum_err),
|
||||
EFX_ETHTOOL_UINT_CHANNEL_STAT(rx_tcp_udp_chksum_err),
|
||||
EFX_ETHTOOL_UINT_CHANNEL_STAT(rx_frm_trunc),
|
||||
};
|
||||
|
||||
/* Number of ethtool statistics */
|
||||
#define EFX_ETHTOOL_NUM_STATS ARRAY_SIZE(efx_ethtool_stats)
|
||||
|
||||
/**************************************************************************
|
||||
*
|
||||
* Ethtool operations
|
||||
*
|
||||
**************************************************************************
|
||||
*/
|
||||
|
||||
/* Identify device by flashing LEDs */
|
||||
static int efx_ethtool_phys_id(struct net_device *net_dev, u32 seconds)
|
||||
{
|
||||
struct efx_nic *efx = net_dev->priv;
|
||||
|
||||
efx->board_info.blink(efx, 1);
|
||||
schedule_timeout_interruptible(seconds * HZ);
|
||||
efx->board_info.blink(efx, 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* This must be called with rtnl_lock held. */
|
||||
int efx_ethtool_get_settings(struct net_device *net_dev,
|
||||
struct ethtool_cmd *ecmd)
|
||||
{
|
||||
struct efx_nic *efx = net_dev->priv;
|
||||
int rc;
|
||||
|
||||
mutex_lock(&efx->mac_lock);
|
||||
rc = falcon_xmac_get_settings(efx, ecmd);
|
||||
mutex_unlock(&efx->mac_lock);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* This must be called with rtnl_lock held. */
|
||||
int efx_ethtool_set_settings(struct net_device *net_dev,
|
||||
struct ethtool_cmd *ecmd)
|
||||
{
|
||||
struct efx_nic *efx = net_dev->priv;
|
||||
int rc;
|
||||
|
||||
mutex_lock(&efx->mac_lock);
|
||||
rc = falcon_xmac_set_settings(efx, ecmd);
|
||||
mutex_unlock(&efx->mac_lock);
|
||||
if (!rc)
|
||||
efx_reconfigure_port(efx);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static void efx_ethtool_get_drvinfo(struct net_device *net_dev,
|
||||
struct ethtool_drvinfo *info)
|
||||
{
|
||||
struct efx_nic *efx = net_dev->priv;
|
||||
|
||||
strlcpy(info->driver, EFX_DRIVER_NAME, sizeof(info->driver));
|
||||
strlcpy(info->version, EFX_DRIVER_VERSION, sizeof(info->version));
|
||||
strlcpy(info->bus_info, pci_name(efx->pci_dev), sizeof(info->bus_info));
|
||||
}
|
||||
|
||||
static int efx_ethtool_get_stats_count(struct net_device *net_dev)
|
||||
{
|
||||
return EFX_ETHTOOL_NUM_STATS;
|
||||
}
|
||||
|
||||
static void efx_ethtool_get_strings(struct net_device *net_dev,
|
||||
u32 string_set, u8 *strings)
|
||||
{
|
||||
struct ethtool_string *ethtool_strings =
|
||||
(struct ethtool_string *)strings;
|
||||
int i;
|
||||
|
||||
if (string_set == ETH_SS_STATS)
|
||||
for (i = 0; i < EFX_ETHTOOL_NUM_STATS; i++)
|
||||
strncpy(ethtool_strings[i].name,
|
||||
efx_ethtool_stats[i].name,
|
||||
sizeof(ethtool_strings[i].name));
|
||||
}
|
||||
|
||||
static void efx_ethtool_get_stats(struct net_device *net_dev,
|
||||
struct ethtool_stats *stats,
|
||||
u64 *data)
|
||||
{
|
||||
struct efx_nic *efx = net_dev->priv;
|
||||
struct efx_mac_stats *mac_stats = &efx->mac_stats;
|
||||
struct efx_ethtool_stat *stat;
|
||||
struct efx_channel *channel;
|
||||
int i;
|
||||
|
||||
EFX_BUG_ON_PARANOID(stats->n_stats != EFX_ETHTOOL_NUM_STATS);
|
||||
|
||||
/* Update MAC and NIC statistics */
|
||||
net_dev->get_stats(net_dev);
|
||||
|
||||
/* Fill detailed statistics buffer */
|
||||
for (i = 0; i < EFX_ETHTOOL_NUM_STATS; i++) {
|
||||
stat = &efx_ethtool_stats[i];
|
||||
switch (stat->source) {
|
||||
case EFX_ETHTOOL_STAT_SOURCE_mac_stats:
|
||||
data[i] = stat->get_stat((void *)mac_stats +
|
||||
stat->offset);
|
||||
break;
|
||||
case EFX_ETHTOOL_STAT_SOURCE_nic:
|
||||
data[i] = stat->get_stat((void *)efx + stat->offset);
|
||||
break;
|
||||
case EFX_ETHTOOL_STAT_SOURCE_channel:
|
||||
data[i] = 0;
|
||||
efx_for_each_channel(channel, efx)
|
||||
data[i] += stat->get_stat((void *)channel +
|
||||
stat->offset);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int efx_ethtool_set_tx_csum(struct net_device *net_dev, u32 enable)
|
||||
{
|
||||
struct efx_nic *efx = net_dev->priv;
|
||||
int rc;
|
||||
|
||||
rc = ethtool_op_set_tx_csum(net_dev, enable);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
efx_flush_queues(efx);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int efx_ethtool_set_rx_csum(struct net_device *net_dev, u32 enable)
|
||||
{
|
||||
struct efx_nic *efx = net_dev->priv;
|
||||
|
||||
/* No way to stop the hardware doing the checks; we just
|
||||
* ignore the result.
|
||||
*/
|
||||
efx->rx_checksum_enabled = (enable ? 1 : 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static u32 efx_ethtool_get_rx_csum(struct net_device *net_dev)
|
||||
{
|
||||
struct efx_nic *efx = net_dev->priv;
|
||||
|
||||
return efx->rx_checksum_enabled;
|
||||
}
|
||||
|
||||
/* Restart autonegotiation */
|
||||
static int efx_ethtool_nway_reset(struct net_device *net_dev)
|
||||
{
|
||||
struct efx_nic *efx = net_dev->priv;
|
||||
|
||||
return mii_nway_restart(&efx->mii);
|
||||
}
|
||||
|
||||
static u32 efx_ethtool_get_link(struct net_device *net_dev)
|
||||
{
|
||||
struct efx_nic *efx = net_dev->priv;
|
||||
|
||||
return efx->link_up;
|
||||
}
|
||||
|
||||
static int efx_ethtool_get_coalesce(struct net_device *net_dev,
|
||||
struct ethtool_coalesce *coalesce)
|
||||
{
|
||||
struct efx_nic *efx = net_dev->priv;
|
||||
struct efx_tx_queue *tx_queue;
|
||||
struct efx_rx_queue *rx_queue;
|
||||
struct efx_channel *channel;
|
||||
|
||||
memset(coalesce, 0, sizeof(*coalesce));
|
||||
|
||||
/* Find lowest IRQ moderation across all used TX queues */
|
||||
coalesce->tx_coalesce_usecs_irq = ~((u32) 0);
|
||||
efx_for_each_tx_queue(tx_queue, efx) {
|
||||
channel = tx_queue->channel;
|
||||
if (channel->irq_moderation < coalesce->tx_coalesce_usecs_irq) {
|
||||
if (channel->used_flags != EFX_USED_BY_RX_TX)
|
||||
coalesce->tx_coalesce_usecs_irq =
|
||||
channel->irq_moderation;
|
||||
else
|
||||
coalesce->tx_coalesce_usecs_irq = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* Find lowest IRQ moderation across all used RX queues */
|
||||
coalesce->rx_coalesce_usecs_irq = ~((u32) 0);
|
||||
efx_for_each_rx_queue(rx_queue, efx) {
|
||||
channel = rx_queue->channel;
|
||||
if (channel->irq_moderation < coalesce->rx_coalesce_usecs_irq)
|
||||
coalesce->rx_coalesce_usecs_irq =
|
||||
channel->irq_moderation;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Set coalescing parameters
|
||||
* The difficulties occur for shared channels
|
||||
*/
|
||||
static int efx_ethtool_set_coalesce(struct net_device *net_dev,
|
||||
struct ethtool_coalesce *coalesce)
|
||||
{
|
||||
struct efx_nic *efx = net_dev->priv;
|
||||
struct efx_channel *channel;
|
||||
struct efx_tx_queue *tx_queue;
|
||||
unsigned tx_usecs, rx_usecs;
|
||||
|
||||
if (coalesce->use_adaptive_rx_coalesce ||
|
||||
coalesce->use_adaptive_tx_coalesce)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
if (coalesce->rx_coalesce_usecs || coalesce->tx_coalesce_usecs) {
|
||||
EFX_ERR(efx, "invalid coalescing setting. "
|
||||
"Only rx/tx_coalesce_usecs_irq are supported\n");
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
rx_usecs = coalesce->rx_coalesce_usecs_irq;
|
||||
tx_usecs = coalesce->tx_coalesce_usecs_irq;
|
||||
|
||||
/* If the channel is shared only allow RX parameters to be set */
|
||||
efx_for_each_tx_queue(tx_queue, efx) {
|
||||
if ((tx_queue->channel->used_flags == EFX_USED_BY_RX_TX) &&
|
||||
tx_usecs) {
|
||||
EFX_ERR(efx, "Channel is shared. "
|
||||
"Only RX coalescing may be set\n");
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
}
|
||||
|
||||
efx_init_irq_moderation(efx, tx_usecs, rx_usecs);
|
||||
|
||||
/* Reset channel to pick up new moderation value. Note that
|
||||
* this may change the value of the irq_moderation field
|
||||
* (e.g. to allow for hardware timer granularity).
|
||||
*/
|
||||
efx_for_each_channel(channel, efx)
|
||||
falcon_set_int_moderation(channel);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int efx_ethtool_set_pauseparam(struct net_device *net_dev,
|
||||
struct ethtool_pauseparam *pause)
|
||||
{
|
||||
struct efx_nic *efx = net_dev->priv;
|
||||
enum efx_fc_type flow_control = efx->flow_control;
|
||||
int rc;
|
||||
|
||||
flow_control &= ~(EFX_FC_RX | EFX_FC_TX | EFX_FC_AUTO);
|
||||
flow_control |= pause->rx_pause ? EFX_FC_RX : 0;
|
||||
flow_control |= pause->tx_pause ? EFX_FC_TX : 0;
|
||||
flow_control |= pause->autoneg ? EFX_FC_AUTO : 0;
|
||||
|
||||
/* Try to push the pause parameters */
|
||||
mutex_lock(&efx->mac_lock);
|
||||
rc = falcon_xmac_set_pause(efx, flow_control);
|
||||
mutex_unlock(&efx->mac_lock);
|
||||
|
||||
if (!rc)
|
||||
efx_reconfigure_port(efx);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static void efx_ethtool_get_pauseparam(struct net_device *net_dev,
|
||||
struct ethtool_pauseparam *pause)
|
||||
{
|
||||
struct efx_nic *efx = net_dev->priv;
|
||||
|
||||
pause->rx_pause = (efx->flow_control & EFX_FC_RX) ? 1 : 0;
|
||||
pause->tx_pause = (efx->flow_control & EFX_FC_TX) ? 1 : 0;
|
||||
pause->autoneg = (efx->flow_control & EFX_FC_AUTO) ? 1 : 0;
|
||||
}
|
||||
|
||||
|
||||
struct ethtool_ops efx_ethtool_ops = {
|
||||
.get_settings = efx_ethtool_get_settings,
|
||||
.set_settings = efx_ethtool_set_settings,
|
||||
.get_drvinfo = efx_ethtool_get_drvinfo,
|
||||
.nway_reset = efx_ethtool_nway_reset,
|
||||
.get_link = efx_ethtool_get_link,
|
||||
.get_coalesce = efx_ethtool_get_coalesce,
|
||||
.set_coalesce = efx_ethtool_set_coalesce,
|
||||
.get_pauseparam = efx_ethtool_get_pauseparam,
|
||||
.set_pauseparam = efx_ethtool_set_pauseparam,
|
||||
.get_rx_csum = efx_ethtool_get_rx_csum,
|
||||
.set_rx_csum = efx_ethtool_set_rx_csum,
|
||||
.get_tx_csum = ethtool_op_get_tx_csum,
|
||||
.set_tx_csum = efx_ethtool_set_tx_csum,
|
||||
.get_sg = ethtool_op_get_sg,
|
||||
.set_sg = ethtool_op_set_sg,
|
||||
.get_flags = ethtool_op_get_flags,
|
||||
.set_flags = ethtool_op_set_flags,
|
||||
.get_strings = efx_ethtool_get_strings,
|
||||
.phys_id = efx_ethtool_phys_id,
|
||||
.get_stats_count = efx_ethtool_get_stats_count,
|
||||
.get_ethtool_stats = efx_ethtool_get_stats,
|
||||
};
|
27
drivers/net/sfc/ethtool.h
Normal file
27
drivers/net/sfc/ethtool.h
Normal file
@ -0,0 +1,27 @@
|
||||
/****************************************************************************
|
||||
* Driver for Solarflare Solarstorm network controllers and boards
|
||||
* Copyright 2005 Fen Systems Ltd.
|
||||
* Copyright 2006 Solarflare Communications Inc.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 as published
|
||||
* by the Free Software Foundation, incorporated herein by reference.
|
||||
*/
|
||||
|
||||
#ifndef EFX_ETHTOOL_H
|
||||
#define EFX_ETHTOOL_H
|
||||
|
||||
#include "net_driver.h"
|
||||
|
||||
/*
|
||||
* Ethtool support
|
||||
*/
|
||||
|
||||
extern int efx_ethtool_get_settings(struct net_device *net_dev,
|
||||
struct ethtool_cmd *ecmd);
|
||||
extern int efx_ethtool_set_settings(struct net_device *net_dev,
|
||||
struct ethtool_cmd *ecmd);
|
||||
|
||||
extern struct ethtool_ops efx_ethtool_ops;
|
||||
|
||||
#endif /* EFX_ETHTOOL_H */
|
2722
drivers/net/sfc/falcon.c
Normal file
2722
drivers/net/sfc/falcon.c
Normal file
File diff suppressed because it is too large
Load Diff
130
drivers/net/sfc/falcon.h
Normal file
130
drivers/net/sfc/falcon.h
Normal file
@ -0,0 +1,130 @@
|
||||
/****************************************************************************
|
||||
* Driver for Solarflare Solarstorm network controllers and boards
|
||||
* Copyright 2005-2006 Fen Systems Ltd.
|
||||
* Copyright 2006-2008 Solarflare Communications Inc.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 as published
|
||||
* by the Free Software Foundation, incorporated herein by reference.
|
||||
*/
|
||||
|
||||
#ifndef EFX_FALCON_H
|
||||
#define EFX_FALCON_H
|
||||
|
||||
#include "net_driver.h"
|
||||
|
||||
/*
|
||||
* Falcon hardware control
|
||||
*/
|
||||
|
||||
enum falcon_revision {
|
||||
FALCON_REV_A0 = 0,
|
||||
FALCON_REV_A1 = 1,
|
||||
FALCON_REV_B0 = 2,
|
||||
};
|
||||
|
||||
#define FALCON_REV(efx) ((efx)->pci_dev->revision)
|
||||
|
||||
extern struct efx_nic_type falcon_a_nic_type;
|
||||
extern struct efx_nic_type falcon_b_nic_type;
|
||||
|
||||
/**************************************************************************
|
||||
*
|
||||
* Externs
|
||||
*
|
||||
**************************************************************************
|
||||
*/
|
||||
|
||||
/* TX data path */
|
||||
extern int falcon_probe_tx(struct efx_tx_queue *tx_queue);
|
||||
extern int falcon_init_tx(struct efx_tx_queue *tx_queue);
|
||||
extern void falcon_fini_tx(struct efx_tx_queue *tx_queue);
|
||||
extern void falcon_remove_tx(struct efx_tx_queue *tx_queue);
|
||||
extern void falcon_push_buffers(struct efx_tx_queue *tx_queue);
|
||||
|
||||
/* RX data path */
|
||||
extern int falcon_probe_rx(struct efx_rx_queue *rx_queue);
|
||||
extern int falcon_init_rx(struct efx_rx_queue *rx_queue);
|
||||
extern void falcon_fini_rx(struct efx_rx_queue *rx_queue);
|
||||
extern void falcon_remove_rx(struct efx_rx_queue *rx_queue);
|
||||
extern void falcon_notify_rx_desc(struct efx_rx_queue *rx_queue);
|
||||
|
||||
/* Event data path */
|
||||
extern int falcon_probe_eventq(struct efx_channel *channel);
|
||||
extern int falcon_init_eventq(struct efx_channel *channel);
|
||||
extern void falcon_fini_eventq(struct efx_channel *channel);
|
||||
extern void falcon_remove_eventq(struct efx_channel *channel);
|
||||
extern int falcon_process_eventq(struct efx_channel *channel, int *rx_quota);
|
||||
extern void falcon_eventq_read_ack(struct efx_channel *channel);
|
||||
|
||||
/* Ports */
|
||||
extern int falcon_probe_port(struct efx_nic *efx);
|
||||
extern void falcon_remove_port(struct efx_nic *efx);
|
||||
|
||||
/* MAC/PHY */
|
||||
extern int falcon_xaui_link_ok(struct efx_nic *efx);
|
||||
extern int falcon_dma_stats(struct efx_nic *efx,
|
||||
unsigned int done_offset);
|
||||
extern void falcon_drain_tx_fifo(struct efx_nic *efx);
|
||||
extern void falcon_deconfigure_mac_wrapper(struct efx_nic *efx);
|
||||
extern void falcon_reconfigure_mac_wrapper(struct efx_nic *efx);
|
||||
|
||||
/* Interrupts and test events */
|
||||
extern int falcon_init_interrupt(struct efx_nic *efx);
|
||||
extern void falcon_enable_interrupts(struct efx_nic *efx);
|
||||
extern void falcon_generate_test_event(struct efx_channel *channel,
|
||||
unsigned int magic);
|
||||
extern void falcon_generate_interrupt(struct efx_nic *efx);
|
||||
extern void falcon_set_int_moderation(struct efx_channel *channel);
|
||||
extern void falcon_disable_interrupts(struct efx_nic *efx);
|
||||
extern void falcon_fini_interrupt(struct efx_nic *efx);
|
||||
|
||||
/* Global Resources */
|
||||
extern int falcon_probe_nic(struct efx_nic *efx);
|
||||
extern int falcon_probe_resources(struct efx_nic *efx);
|
||||
extern int falcon_init_nic(struct efx_nic *efx);
|
||||
extern int falcon_reset_hw(struct efx_nic *efx, enum reset_type method);
|
||||
extern void falcon_remove_resources(struct efx_nic *efx);
|
||||
extern void falcon_remove_nic(struct efx_nic *efx);
|
||||
extern void falcon_update_nic_stats(struct efx_nic *efx);
|
||||
extern void falcon_set_multicast_hash(struct efx_nic *efx);
|
||||
extern int falcon_reset_xaui(struct efx_nic *efx);
|
||||
|
||||
/**************************************************************************
|
||||
*
|
||||
* Falcon MAC stats
|
||||
*
|
||||
**************************************************************************
|
||||
*/
|
||||
|
||||
#define FALCON_STAT_OFFSET(falcon_stat) EFX_VAL(falcon_stat, offset)
|
||||
#define FALCON_STAT_WIDTH(falcon_stat) EFX_VAL(falcon_stat, WIDTH)
|
||||
|
||||
/* Retrieve statistic from statistics block */
|
||||
#define FALCON_STAT(efx, falcon_stat, efx_stat) do { \
|
||||
if (FALCON_STAT_WIDTH(falcon_stat) == 16) \
|
||||
(efx)->mac_stats.efx_stat += le16_to_cpu( \
|
||||
*((__force __le16 *) \
|
||||
(efx->stats_buffer.addr + \
|
||||
FALCON_STAT_OFFSET(falcon_stat)))); \
|
||||
else if (FALCON_STAT_WIDTH(falcon_stat) == 32) \
|
||||
(efx)->mac_stats.efx_stat += le32_to_cpu( \
|
||||
*((__force __le32 *) \
|
||||
(efx->stats_buffer.addr + \
|
||||
FALCON_STAT_OFFSET(falcon_stat)))); \
|
||||
else \
|
||||
(efx)->mac_stats.efx_stat += le64_to_cpu( \
|
||||
*((__force __le64 *) \
|
||||
(efx->stats_buffer.addr + \
|
||||
FALCON_STAT_OFFSET(falcon_stat)))); \
|
||||
} while (0)
|
||||
|
||||
#define FALCON_MAC_STATS_SIZE 0x100
|
||||
|
||||
#define MAC_DATA_LBN 0
|
||||
#define MAC_DATA_WIDTH 32
|
||||
|
||||
extern void falcon_generate_event(struct efx_channel *channel,
|
||||
efx_qword_t *event);
|
||||
|
||||
#endif /* EFX_FALCON_H */
|
1135
drivers/net/sfc/falcon_hwdefs.h
Normal file
1135
drivers/net/sfc/falcon_hwdefs.h
Normal file
File diff suppressed because it is too large
Load Diff
243
drivers/net/sfc/falcon_io.h
Normal file
243
drivers/net/sfc/falcon_io.h
Normal file
@ -0,0 +1,243 @@
|
||||
/****************************************************************************
|
||||
* Driver for Solarflare Solarstorm network controllers and boards
|
||||
* Copyright 2005-2006 Fen Systems Ltd.
|
||||
* Copyright 2006-2008 Solarflare Communications Inc.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 as published
|
||||
* by the Free Software Foundation, incorporated herein by reference.
|
||||
*/
|
||||
|
||||
#ifndef EFX_FALCON_IO_H
|
||||
#define EFX_FALCON_IO_H
|
||||
|
||||
#include <linux/io.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include "net_driver.h"
|
||||
|
||||
/**************************************************************************
|
||||
*
|
||||
* Falcon hardware access
|
||||
*
|
||||
**************************************************************************
|
||||
*
|
||||
* Notes on locking strategy:
|
||||
*
|
||||
* Most Falcon registers require 16-byte (or 8-byte, for SRAM
|
||||
* registers) atomic writes which necessitates locking.
|
||||
* Under normal operation few writes to the Falcon BAR are made and these
|
||||
* registers (EVQ_RPTR_REG, RX_DESC_UPD_REG and TX_DESC_UPD_REG) are special
|
||||
* cased to allow 4-byte (hence lockless) accesses.
|
||||
*
|
||||
* It *is* safe to write to these 4-byte registers in the middle of an
|
||||
* access to an 8-byte or 16-byte register. We therefore use a
|
||||
* spinlock to protect accesses to the larger registers, but no locks
|
||||
* for the 4-byte registers.
|
||||
*
|
||||
* A write barrier is needed to ensure that DW3 is written after DW0/1/2
|
||||
* due to the way the 16byte registers are "collected" in the Falcon BIU
|
||||
*
|
||||
* We also lock when carrying out reads, to ensure consistency of the
|
||||
* data (made possible since the BIU reads all 128 bits into a cache).
|
||||
* Reads are very rare, so this isn't a significant performance
|
||||
* impact. (Most data transferred from NIC to host is DMAed directly
|
||||
* into host memory).
|
||||
*
|
||||
* I/O BAR access uses locks for both reads and writes (but is only provided
|
||||
* for testing purposes).
|
||||
*/
|
||||
|
||||
/* Special buffer descriptors (Falcon SRAM) */
|
||||
#define BUF_TBL_KER_A1 0x18000
|
||||
#define BUF_TBL_KER_B0 0x800000
|
||||
|
||||
|
||||
#if BITS_PER_LONG == 64
|
||||
#define FALCON_USE_QWORD_IO 1
|
||||
#endif
|
||||
|
||||
#define _falcon_writeq(efx, value, reg) \
|
||||
__raw_writeq((__force u64) (value), (efx)->membase + (reg))
|
||||
#define _falcon_writel(efx, value, reg) \
|
||||
__raw_writel((__force u32) (value), (efx)->membase + (reg))
|
||||
#define _falcon_readq(efx, reg) \
|
||||
((__force __le64) __raw_readq((efx)->membase + (reg)))
|
||||
#define _falcon_readl(efx, reg) \
|
||||
((__force __le32) __raw_readl((efx)->membase + (reg)))
|
||||
|
||||
/* Writes to a normal 16-byte Falcon register, locking as appropriate. */
|
||||
static inline void falcon_write(struct efx_nic *efx, efx_oword_t *value,
|
||||
unsigned int reg)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
EFX_REGDUMP(efx, "writing register %x with " EFX_OWORD_FMT "\n", reg,
|
||||
EFX_OWORD_VAL(*value));
|
||||
|
||||
spin_lock_irqsave(&efx->biu_lock, flags);
|
||||
#ifdef FALCON_USE_QWORD_IO
|
||||
_falcon_writeq(efx, value->u64[0], reg + 0);
|
||||
wmb();
|
||||
_falcon_writeq(efx, value->u64[1], reg + 8);
|
||||
#else
|
||||
_falcon_writel(efx, value->u32[0], reg + 0);
|
||||
_falcon_writel(efx, value->u32[1], reg + 4);
|
||||
_falcon_writel(efx, value->u32[2], reg + 8);
|
||||
wmb();
|
||||
_falcon_writel(efx, value->u32[3], reg + 12);
|
||||
#endif
|
||||
mmiowb();
|
||||
spin_unlock_irqrestore(&efx->biu_lock, flags);
|
||||
}
|
||||
|
||||
/* Writes to an 8-byte Falcon SRAM register, locking as appropriate. */
|
||||
static inline void falcon_write_sram(struct efx_nic *efx, efx_qword_t *value,
|
||||
unsigned int index)
|
||||
{
|
||||
unsigned int reg = efx->type->buf_tbl_base + (index * sizeof(*value));
|
||||
unsigned long flags;
|
||||
|
||||
EFX_REGDUMP(efx, "writing SRAM register %x with " EFX_QWORD_FMT "\n",
|
||||
reg, EFX_QWORD_VAL(*value));
|
||||
|
||||
spin_lock_irqsave(&efx->biu_lock, flags);
|
||||
#ifdef FALCON_USE_QWORD_IO
|
||||
_falcon_writeq(efx, value->u64[0], reg + 0);
|
||||
#else
|
||||
_falcon_writel(efx, value->u32[0], reg + 0);
|
||||
wmb();
|
||||
_falcon_writel(efx, value->u32[1], reg + 4);
|
||||
#endif
|
||||
mmiowb();
|
||||
spin_unlock_irqrestore(&efx->biu_lock, flags);
|
||||
}
|
||||
|
||||
/* Write dword to Falcon register that allows partial writes
|
||||
*
|
||||
* Some Falcon registers (EVQ_RPTR_REG, RX_DESC_UPD_REG and
|
||||
* TX_DESC_UPD_REG) can be written to as a single dword. This allows
|
||||
* for lockless writes.
|
||||
*/
|
||||
static inline void falcon_writel(struct efx_nic *efx, efx_dword_t *value,
|
||||
unsigned int reg)
|
||||
{
|
||||
EFX_REGDUMP(efx, "writing partial register %x with "EFX_DWORD_FMT"\n",
|
||||
reg, EFX_DWORD_VAL(*value));
|
||||
|
||||
/* No lock required */
|
||||
_falcon_writel(efx, value->u32[0], reg);
|
||||
}
|
||||
|
||||
/* Read from a Falcon register
|
||||
*
|
||||
* This reads an entire 16-byte Falcon register in one go, locking as
|
||||
* appropriate. It is essential to read the first dword first, as this
|
||||
* prompts Falcon to load the current value into the shadow register.
|
||||
*/
|
||||
static inline void falcon_read(struct efx_nic *efx, efx_oword_t *value,
|
||||
unsigned int reg)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&efx->biu_lock, flags);
|
||||
value->u32[0] = _falcon_readl(efx, reg + 0);
|
||||
rmb();
|
||||
value->u32[1] = _falcon_readl(efx, reg + 4);
|
||||
value->u32[2] = _falcon_readl(efx, reg + 8);
|
||||
value->u32[3] = _falcon_readl(efx, reg + 12);
|
||||
spin_unlock_irqrestore(&efx->biu_lock, flags);
|
||||
|
||||
EFX_REGDUMP(efx, "read from register %x, got " EFX_OWORD_FMT "\n", reg,
|
||||
EFX_OWORD_VAL(*value));
|
||||
}
|
||||
|
||||
/* This reads an 8-byte Falcon SRAM entry in one go. */
|
||||
static inline void falcon_read_sram(struct efx_nic *efx, efx_qword_t *value,
|
||||
unsigned int index)
|
||||
{
|
||||
unsigned int reg = efx->type->buf_tbl_base + (index * sizeof(*value));
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&efx->biu_lock, flags);
|
||||
#ifdef FALCON_USE_QWORD_IO
|
||||
value->u64[0] = _falcon_readq(efx, reg + 0);
|
||||
#else
|
||||
value->u32[0] = _falcon_readl(efx, reg + 0);
|
||||
rmb();
|
||||
value->u32[1] = _falcon_readl(efx, reg + 4);
|
||||
#endif
|
||||
spin_unlock_irqrestore(&efx->biu_lock, flags);
|
||||
|
||||
EFX_REGDUMP(efx, "read from SRAM register %x, got "EFX_QWORD_FMT"\n",
|
||||
reg, EFX_QWORD_VAL(*value));
|
||||
}
|
||||
|
||||
/* Read dword from Falcon register that allows partial writes (sic) */
|
||||
static inline void falcon_readl(struct efx_nic *efx, efx_dword_t *value,
|
||||
unsigned int reg)
|
||||
{
|
||||
value->u32[0] = _falcon_readl(efx, reg);
|
||||
EFX_REGDUMP(efx, "read from register %x, got "EFX_DWORD_FMT"\n",
|
||||
reg, EFX_DWORD_VAL(*value));
|
||||
}
|
||||
|
||||
/* Write to a register forming part of a table */
|
||||
static inline void falcon_write_table(struct efx_nic *efx, efx_oword_t *value,
|
||||
unsigned int reg, unsigned int index)
|
||||
{
|
||||
falcon_write(efx, value, reg + index * sizeof(efx_oword_t));
|
||||
}
|
||||
|
||||
/* Read to a register forming part of a table */
|
||||
static inline void falcon_read_table(struct efx_nic *efx, efx_oword_t *value,
|
||||
unsigned int reg, unsigned int index)
|
||||
{
|
||||
falcon_read(efx, value, reg + index * sizeof(efx_oword_t));
|
||||
}
|
||||
|
||||
/* Write to a dword register forming part of a table */
|
||||
static inline void falcon_writel_table(struct efx_nic *efx, efx_dword_t *value,
|
||||
unsigned int reg, unsigned int index)
|
||||
{
|
||||
falcon_writel(efx, value, reg + index * sizeof(efx_oword_t));
|
||||
}
|
||||
|
||||
/* Page-mapped register block size */
|
||||
#define FALCON_PAGE_BLOCK_SIZE 0x2000
|
||||
|
||||
/* Calculate offset to page-mapped register block */
|
||||
#define FALCON_PAGED_REG(page, reg) \
|
||||
((page) * FALCON_PAGE_BLOCK_SIZE + (reg))
|
||||
|
||||
/* As for falcon_write(), but for a page-mapped register. */
|
||||
static inline void falcon_write_page(struct efx_nic *efx, efx_oword_t *value,
|
||||
unsigned int reg, unsigned int page)
|
||||
{
|
||||
falcon_write(efx, value, FALCON_PAGED_REG(page, reg));
|
||||
}
|
||||
|
||||
/* As for falcon_writel(), but for a page-mapped register. */
|
||||
static inline void falcon_writel_page(struct efx_nic *efx, efx_dword_t *value,
|
||||
unsigned int reg, unsigned int page)
|
||||
{
|
||||
falcon_writel(efx, value, FALCON_PAGED_REG(page, reg));
|
||||
}
|
||||
|
||||
/* Write dword to Falcon page-mapped register with an extra lock.
|
||||
*
|
||||
* As for falcon_writel_page(), but for a register that suffers from
|
||||
* SFC bug 3181. Take out a lock so the BIU collector cannot be
|
||||
* confused. */
|
||||
static inline void falcon_writel_page_locked(struct efx_nic *efx,
|
||||
efx_dword_t *value,
|
||||
unsigned int reg,
|
||||
unsigned int page)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&efx->biu_lock, flags);
|
||||
falcon_writel(efx, value, FALCON_PAGED_REG(page, reg));
|
||||
spin_unlock_irqrestore(&efx->biu_lock, flags);
|
||||
}
|
||||
|
||||
#endif /* EFX_FALCON_IO_H */
|
585
drivers/net/sfc/falcon_xmac.c
Normal file
585
drivers/net/sfc/falcon_xmac.c
Normal file
@ -0,0 +1,585 @@
|
||||
/****************************************************************************
|
||||
* Driver for Solarflare Solarstorm network controllers and boards
|
||||
* Copyright 2005-2006 Fen Systems Ltd.
|
||||
* Copyright 2006-2008 Solarflare Communications Inc.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 as published
|
||||
* by the Free Software Foundation, incorporated herein by reference.
|
||||
*/
|
||||
|
||||
#include <linux/delay.h>
|
||||
#include "net_driver.h"
|
||||
#include "efx.h"
|
||||
#include "falcon.h"
|
||||
#include "falcon_hwdefs.h"
|
||||
#include "falcon_io.h"
|
||||
#include "mac.h"
|
||||
#include "gmii.h"
|
||||
#include "mdio_10g.h"
|
||||
#include "phy.h"
|
||||
#include "boards.h"
|
||||
#include "workarounds.h"
|
||||
|
||||
/**************************************************************************
|
||||
*
|
||||
* MAC register access
|
||||
*
|
||||
**************************************************************************/
|
||||
|
||||
/* Offset of an XMAC register within Falcon */
|
||||
#define FALCON_XMAC_REG(mac_reg) \
|
||||
(FALCON_XMAC_REGBANK + ((mac_reg) * FALCON_XMAC_REG_SIZE))
|
||||
|
||||
void falcon_xmac_writel(struct efx_nic *efx,
|
||||
efx_dword_t *value, unsigned int mac_reg)
|
||||
{
|
||||
efx_oword_t temp;
|
||||
|
||||
EFX_POPULATE_OWORD_1(temp, MAC_DATA, EFX_DWORD_FIELD(*value, MAC_DATA));
|
||||
falcon_write(efx, &temp, FALCON_XMAC_REG(mac_reg));
|
||||
}
|
||||
|
||||
void falcon_xmac_readl(struct efx_nic *efx,
|
||||
efx_dword_t *value, unsigned int mac_reg)
|
||||
{
|
||||
efx_oword_t temp;
|
||||
|
||||
falcon_read(efx, &temp, FALCON_XMAC_REG(mac_reg));
|
||||
EFX_POPULATE_DWORD_1(*value, MAC_DATA, EFX_OWORD_FIELD(temp, MAC_DATA));
|
||||
}
|
||||
|
||||
/**************************************************************************
|
||||
*
|
||||
* MAC operations
|
||||
*
|
||||
*************************************************************************/
|
||||
static int falcon_reset_xmac(struct efx_nic *efx)
|
||||
{
|
||||
efx_dword_t reg;
|
||||
int count;
|
||||
|
||||
EFX_POPULATE_DWORD_1(reg, XM_CORE_RST, 1);
|
||||
falcon_xmac_writel(efx, ®, XM_GLB_CFG_REG_MAC);
|
||||
|
||||
for (count = 0; count < 10000; count++) { /* wait upto 100ms */
|
||||
falcon_xmac_readl(efx, ®, XM_GLB_CFG_REG_MAC);
|
||||
if (EFX_DWORD_FIELD(reg, XM_CORE_RST) == 0)
|
||||
return 0;
|
||||
udelay(10);
|
||||
}
|
||||
|
||||
EFX_ERR(efx, "timed out waiting for XMAC core reset\n");
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
/* Configure the XAUI driver that is an output from Falcon */
|
||||
static void falcon_setup_xaui(struct efx_nic *efx)
|
||||
{
|
||||
efx_dword_t sdctl, txdrv;
|
||||
|
||||
/* Move the XAUI into low power, unless there is no PHY, in
|
||||
* which case the XAUI will have to drive a cable. */
|
||||
if (efx->phy_type == PHY_TYPE_NONE)
|
||||
return;
|
||||
|
||||
falcon_xmac_readl(efx, &sdctl, XX_SD_CTL_REG_MAC);
|
||||
EFX_SET_DWORD_FIELD(sdctl, XX_HIDRVD, XX_SD_CTL_DRV_DEFAULT);
|
||||
EFX_SET_DWORD_FIELD(sdctl, XX_LODRVD, XX_SD_CTL_DRV_DEFAULT);
|
||||
EFX_SET_DWORD_FIELD(sdctl, XX_HIDRVC, XX_SD_CTL_DRV_DEFAULT);
|
||||
EFX_SET_DWORD_FIELD(sdctl, XX_LODRVC, XX_SD_CTL_DRV_DEFAULT);
|
||||
EFX_SET_DWORD_FIELD(sdctl, XX_HIDRVB, XX_SD_CTL_DRV_DEFAULT);
|
||||
EFX_SET_DWORD_FIELD(sdctl, XX_LODRVB, XX_SD_CTL_DRV_DEFAULT);
|
||||
EFX_SET_DWORD_FIELD(sdctl, XX_HIDRVA, XX_SD_CTL_DRV_DEFAULT);
|
||||
EFX_SET_DWORD_FIELD(sdctl, XX_LODRVA, XX_SD_CTL_DRV_DEFAULT);
|
||||
falcon_xmac_writel(efx, &sdctl, XX_SD_CTL_REG_MAC);
|
||||
|
||||
EFX_POPULATE_DWORD_8(txdrv,
|
||||
XX_DEQD, XX_TXDRV_DEQ_DEFAULT,
|
||||
XX_DEQC, XX_TXDRV_DEQ_DEFAULT,
|
||||
XX_DEQB, XX_TXDRV_DEQ_DEFAULT,
|
||||
XX_DEQA, XX_TXDRV_DEQ_DEFAULT,
|
||||
XX_DTXD, XX_TXDRV_DTX_DEFAULT,
|
||||
XX_DTXC, XX_TXDRV_DTX_DEFAULT,
|
||||
XX_DTXB, XX_TXDRV_DTX_DEFAULT,
|
||||
XX_DTXA, XX_TXDRV_DTX_DEFAULT);
|
||||
falcon_xmac_writel(efx, &txdrv, XX_TXDRV_CTL_REG_MAC);
|
||||
}
|
||||
|
||||
static void falcon_hold_xaui_in_rst(struct efx_nic *efx)
|
||||
{
|
||||
efx_dword_t reg;
|
||||
|
||||
EFX_ZERO_DWORD(reg);
|
||||
EFX_SET_DWORD_FIELD(reg, XX_PWRDNA_EN, 1);
|
||||
EFX_SET_DWORD_FIELD(reg, XX_PWRDNB_EN, 1);
|
||||
EFX_SET_DWORD_FIELD(reg, XX_PWRDNC_EN, 1);
|
||||
EFX_SET_DWORD_FIELD(reg, XX_PWRDND_EN, 1);
|
||||
EFX_SET_DWORD_FIELD(reg, XX_RSTPLLAB_EN, 1);
|
||||
EFX_SET_DWORD_FIELD(reg, XX_RSTPLLCD_EN, 1);
|
||||
EFX_SET_DWORD_FIELD(reg, XX_RESETA_EN, 1);
|
||||
EFX_SET_DWORD_FIELD(reg, XX_RESETB_EN, 1);
|
||||
EFX_SET_DWORD_FIELD(reg, XX_RESETC_EN, 1);
|
||||
EFX_SET_DWORD_FIELD(reg, XX_RESETD_EN, 1);
|
||||
EFX_SET_DWORD_FIELD(reg, XX_RSTXGXSRX_EN, 1);
|
||||
EFX_SET_DWORD_FIELD(reg, XX_RSTXGXSTX_EN, 1);
|
||||
falcon_xmac_writel(efx, ®, XX_PWR_RST_REG_MAC);
|
||||
udelay(10);
|
||||
}
|
||||
|
||||
static int _falcon_reset_xaui_a(struct efx_nic *efx)
|
||||
{
|
||||
efx_dword_t reg;
|
||||
|
||||
falcon_hold_xaui_in_rst(efx);
|
||||
falcon_xmac_readl(efx, ®, XX_PWR_RST_REG_MAC);
|
||||
|
||||
/* Follow the RAMBUS XAUI data reset sequencing
|
||||
* Channels A and B first: power down, reset PLL, reset, clear
|
||||
*/
|
||||
EFX_SET_DWORD_FIELD(reg, XX_PWRDNA_EN, 0);
|
||||
EFX_SET_DWORD_FIELD(reg, XX_PWRDNB_EN, 0);
|
||||
falcon_xmac_writel(efx, ®, XX_PWR_RST_REG_MAC);
|
||||
udelay(10);
|
||||
|
||||
EFX_SET_DWORD_FIELD(reg, XX_RSTPLLAB_EN, 0);
|
||||
falcon_xmac_writel(efx, ®, XX_PWR_RST_REG_MAC);
|
||||
udelay(10);
|
||||
|
||||
EFX_SET_DWORD_FIELD(reg, XX_RESETA_EN, 0);
|
||||
EFX_SET_DWORD_FIELD(reg, XX_RESETB_EN, 0);
|
||||
falcon_xmac_writel(efx, ®, XX_PWR_RST_REG_MAC);
|
||||
udelay(10);
|
||||
|
||||
/* Channels C and D: power down, reset PLL, reset, clear */
|
||||
EFX_SET_DWORD_FIELD(reg, XX_PWRDNC_EN, 0);
|
||||
EFX_SET_DWORD_FIELD(reg, XX_PWRDND_EN, 0);
|
||||
falcon_xmac_writel(efx, ®, XX_PWR_RST_REG_MAC);
|
||||
udelay(10);
|
||||
|
||||
EFX_SET_DWORD_FIELD(reg, XX_RSTPLLCD_EN, 0);
|
||||
falcon_xmac_writel(efx, ®, XX_PWR_RST_REG_MAC);
|
||||
udelay(10);
|
||||
|
||||
EFX_SET_DWORD_FIELD(reg, XX_RESETC_EN, 0);
|
||||
EFX_SET_DWORD_FIELD(reg, XX_RESETD_EN, 0);
|
||||
falcon_xmac_writel(efx, ®, XX_PWR_RST_REG_MAC);
|
||||
udelay(10);
|
||||
|
||||
/* Setup XAUI */
|
||||
falcon_setup_xaui(efx);
|
||||
udelay(10);
|
||||
|
||||
/* Take XGXS out of reset */
|
||||
EFX_ZERO_DWORD(reg);
|
||||
falcon_xmac_writel(efx, ®, XX_PWR_RST_REG_MAC);
|
||||
udelay(10);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int _falcon_reset_xaui_b(struct efx_nic *efx)
|
||||
{
|
||||
efx_dword_t reg;
|
||||
int count;
|
||||
|
||||
EFX_POPULATE_DWORD_1(reg, XX_RST_XX_EN, 1);
|
||||
falcon_xmac_writel(efx, ®, XX_PWR_RST_REG_MAC);
|
||||
|
||||
/* Give some time for the link to establish */
|
||||
for (count = 0; count < 1000; count++) { /* wait upto 10ms */
|
||||
falcon_xmac_readl(efx, ®, XX_PWR_RST_REG_MAC);
|
||||
if (EFX_DWORD_FIELD(reg, XX_RST_XX_EN) == 0) {
|
||||
falcon_setup_xaui(efx);
|
||||
return 0;
|
||||
}
|
||||
udelay(10);
|
||||
}
|
||||
EFX_ERR(efx, "timed out waiting for XAUI/XGXS reset\n");
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
int falcon_reset_xaui(struct efx_nic *efx)
|
||||
{
|
||||
int rc;
|
||||
|
||||
if (EFX_WORKAROUND_9388(efx)) {
|
||||
falcon_hold_xaui_in_rst(efx);
|
||||
efx->phy_op->reset_xaui(efx);
|
||||
rc = _falcon_reset_xaui_a(efx);
|
||||
} else {
|
||||
rc = _falcon_reset_xaui_b(efx);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int falcon_xgmii_status(struct efx_nic *efx)
|
||||
{
|
||||
efx_dword_t reg;
|
||||
|
||||
if (FALCON_REV(efx) < FALCON_REV_B0)
|
||||
return 1;
|
||||
|
||||
/* The ISR latches, so clear it and re-read */
|
||||
falcon_xmac_readl(efx, ®, XM_MGT_INT_REG_MAC_B0);
|
||||
falcon_xmac_readl(efx, ®, XM_MGT_INT_REG_MAC_B0);
|
||||
|
||||
if (EFX_DWORD_FIELD(reg, XM_LCLFLT) ||
|
||||
EFX_DWORD_FIELD(reg, XM_RMTFLT)) {
|
||||
EFX_INFO(efx, "MGT_INT: "EFX_DWORD_FMT"\n", EFX_DWORD_VAL(reg));
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void falcon_mask_status_intr(struct efx_nic *efx, int enable)
|
||||
{
|
||||
efx_dword_t reg;
|
||||
|
||||
if (FALCON_REV(efx) < FALCON_REV_B0)
|
||||
return;
|
||||
|
||||
/* Flush the ISR */
|
||||
if (enable)
|
||||
falcon_xmac_readl(efx, ®, XM_MGT_INT_REG_MAC_B0);
|
||||
|
||||
EFX_POPULATE_DWORD_2(reg,
|
||||
XM_MSK_RMTFLT, !enable,
|
||||
XM_MSK_LCLFLT, !enable);
|
||||
falcon_xmac_writel(efx, ®, XM_MGT_INT_MSK_REG_MAC_B0);
|
||||
}
|
||||
|
||||
int falcon_init_xmac(struct efx_nic *efx)
|
||||
{
|
||||
int rc;
|
||||
|
||||
/* Initialize the PHY first so the clock is around */
|
||||
rc = efx->phy_op->init(efx);
|
||||
if (rc)
|
||||
goto fail1;
|
||||
|
||||
rc = falcon_reset_xaui(efx);
|
||||
if (rc)
|
||||
goto fail2;
|
||||
|
||||
/* Wait again. Give the PHY and MAC time to come back */
|
||||
schedule_timeout_uninterruptible(HZ / 10);
|
||||
|
||||
rc = falcon_reset_xmac(efx);
|
||||
if (rc)
|
||||
goto fail2;
|
||||
|
||||
falcon_mask_status_intr(efx, 1);
|
||||
return 0;
|
||||
|
||||
fail2:
|
||||
efx->phy_op->fini(efx);
|
||||
fail1:
|
||||
return rc;
|
||||
}
|
||||
|
||||
int falcon_xaui_link_ok(struct efx_nic *efx)
|
||||
{
|
||||
efx_dword_t reg;
|
||||
int align_done, sync_status, link_ok = 0;
|
||||
|
||||
/* Read link status */
|
||||
falcon_xmac_readl(efx, ®, XX_CORE_STAT_REG_MAC);
|
||||
|
||||
align_done = EFX_DWORD_FIELD(reg, XX_ALIGN_DONE);
|
||||
sync_status = EFX_DWORD_FIELD(reg, XX_SYNC_STAT);
|
||||
if (align_done && (sync_status == XX_SYNC_STAT_DECODE_SYNCED))
|
||||
link_ok = 1;
|
||||
|
||||
/* Clear link status ready for next read */
|
||||
EFX_SET_DWORD_FIELD(reg, XX_COMMA_DET, XX_COMMA_DET_RESET);
|
||||
EFX_SET_DWORD_FIELD(reg, XX_CHARERR, XX_CHARERR_RESET);
|
||||
EFX_SET_DWORD_FIELD(reg, XX_DISPERR, XX_DISPERR_RESET);
|
||||
falcon_xmac_writel(efx, ®, XX_CORE_STAT_REG_MAC);
|
||||
|
||||
/* If the link is up, then check the phy side of the xaui link
|
||||
* (error conditions from the wire side propoagate back through
|
||||
* the phy to the xaui side). */
|
||||
if (efx->link_up && link_ok) {
|
||||
int has_phyxs = efx->phy_op->mmds & (1 << MDIO_MMD_PHYXS);
|
||||
if (has_phyxs)
|
||||
link_ok = mdio_clause45_phyxgxs_lane_sync(efx);
|
||||
}
|
||||
|
||||
/* If the PHY and XAUI links are up, then check the mac's xgmii
|
||||
* fault state */
|
||||
if (efx->link_up && link_ok)
|
||||
link_ok = falcon_xgmii_status(efx);
|
||||
|
||||
return link_ok;
|
||||
}
|
||||
|
||||
static void falcon_reconfigure_xmac_core(struct efx_nic *efx)
|
||||
{
|
||||
unsigned int max_frame_len;
|
||||
efx_dword_t reg;
|
||||
int rx_fc = (efx->flow_control & EFX_FC_RX) ? 1 : 0;
|
||||
|
||||
/* Configure MAC - cut-thru mode is hard wired on */
|
||||
EFX_POPULATE_DWORD_3(reg,
|
||||
XM_RX_JUMBO_MODE, 1,
|
||||
XM_TX_STAT_EN, 1,
|
||||
XM_RX_STAT_EN, 1);
|
||||
falcon_xmac_writel(efx, ®, XM_GLB_CFG_REG_MAC);
|
||||
|
||||
/* Configure TX */
|
||||
EFX_POPULATE_DWORD_6(reg,
|
||||
XM_TXEN, 1,
|
||||
XM_TX_PRMBL, 1,
|
||||
XM_AUTO_PAD, 1,
|
||||
XM_TXCRC, 1,
|
||||
XM_FCNTL, 1,
|
||||
XM_IPG, 0x3);
|
||||
falcon_xmac_writel(efx, ®, XM_TX_CFG_REG_MAC);
|
||||
|
||||
/* Configure RX */
|
||||
EFX_POPULATE_DWORD_5(reg,
|
||||
XM_RXEN, 1,
|
||||
XM_AUTO_DEPAD, 0,
|
||||
XM_ACPT_ALL_MCAST, 1,
|
||||
XM_ACPT_ALL_UCAST, efx->promiscuous,
|
||||
XM_PASS_CRC_ERR, 1);
|
||||
falcon_xmac_writel(efx, ®, XM_RX_CFG_REG_MAC);
|
||||
|
||||
/* Set frame length */
|
||||
max_frame_len = EFX_MAX_FRAME_LEN(efx->net_dev->mtu);
|
||||
EFX_POPULATE_DWORD_1(reg, XM_MAX_RX_FRM_SIZE, max_frame_len);
|
||||
falcon_xmac_writel(efx, ®, XM_RX_PARAM_REG_MAC);
|
||||
EFX_POPULATE_DWORD_2(reg,
|
||||
XM_MAX_TX_FRM_SIZE, max_frame_len,
|
||||
XM_TX_JUMBO_MODE, 1);
|
||||
falcon_xmac_writel(efx, ®, XM_TX_PARAM_REG_MAC);
|
||||
|
||||
EFX_POPULATE_DWORD_2(reg,
|
||||
XM_PAUSE_TIME, 0xfffe, /* MAX PAUSE TIME */
|
||||
XM_DIS_FCNTL, rx_fc ? 0 : 1);
|
||||
falcon_xmac_writel(efx, ®, XM_FC_REG_MAC);
|
||||
|
||||
/* Set MAC address */
|
||||
EFX_POPULATE_DWORD_4(reg,
|
||||
XM_ADR_0, efx->net_dev->dev_addr[0],
|
||||
XM_ADR_1, efx->net_dev->dev_addr[1],
|
||||
XM_ADR_2, efx->net_dev->dev_addr[2],
|
||||
XM_ADR_3, efx->net_dev->dev_addr[3]);
|
||||
falcon_xmac_writel(efx, ®, XM_ADR_LO_REG_MAC);
|
||||
EFX_POPULATE_DWORD_2(reg,
|
||||
XM_ADR_4, efx->net_dev->dev_addr[4],
|
||||
XM_ADR_5, efx->net_dev->dev_addr[5]);
|
||||
falcon_xmac_writel(efx, ®, XM_ADR_HI_REG_MAC);
|
||||
}
|
||||
|
||||
/* Try and bring the Falcon side of the Falcon-Phy XAUI link fails
|
||||
* to come back up. Bash it until it comes back up */
|
||||
static int falcon_check_xaui_link_up(struct efx_nic *efx)
|
||||
{
|
||||
int max_tries, tries;
|
||||
tries = EFX_WORKAROUND_5147(efx) ? 5 : 1;
|
||||
max_tries = tries;
|
||||
|
||||
if (efx->phy_type == PHY_TYPE_NONE)
|
||||
return 0;
|
||||
|
||||
while (tries) {
|
||||
if (falcon_xaui_link_ok(efx))
|
||||
return 1;
|
||||
|
||||
EFX_LOG(efx, "%s Clobbering XAUI (%d tries left).\n",
|
||||
__func__, tries);
|
||||
(void) falcon_reset_xaui(efx);
|
||||
udelay(200);
|
||||
tries--;
|
||||
}
|
||||
|
||||
EFX_ERR(efx, "Failed to bring XAUI link back up in %d tries!\n",
|
||||
max_tries);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void falcon_reconfigure_xmac(struct efx_nic *efx)
|
||||
{
|
||||
int xaui_link_ok;
|
||||
|
||||
falcon_mask_status_intr(efx, 0);
|
||||
|
||||
falcon_deconfigure_mac_wrapper(efx);
|
||||
efx->phy_op->reconfigure(efx);
|
||||
falcon_reconfigure_xmac_core(efx);
|
||||
falcon_reconfigure_mac_wrapper(efx);
|
||||
|
||||
/* Ensure XAUI link is up */
|
||||
xaui_link_ok = falcon_check_xaui_link_up(efx);
|
||||
|
||||
if (xaui_link_ok && efx->link_up)
|
||||
falcon_mask_status_intr(efx, 1);
|
||||
}
|
||||
|
||||
void falcon_fini_xmac(struct efx_nic *efx)
|
||||
{
|
||||
/* Isolate the MAC - PHY */
|
||||
falcon_deconfigure_mac_wrapper(efx);
|
||||
|
||||
/* Potentially power down the PHY */
|
||||
efx->phy_op->fini(efx);
|
||||
}
|
||||
|
||||
void falcon_update_stats_xmac(struct efx_nic *efx)
|
||||
{
|
||||
struct efx_mac_stats *mac_stats = &efx->mac_stats;
|
||||
int rc;
|
||||
|
||||
rc = falcon_dma_stats(efx, XgDmaDone_offset);
|
||||
if (rc)
|
||||
return;
|
||||
|
||||
/* Update MAC stats from DMAed values */
|
||||
FALCON_STAT(efx, XgRxOctets, rx_bytes);
|
||||
FALCON_STAT(efx, XgRxOctetsOK, rx_good_bytes);
|
||||
FALCON_STAT(efx, XgRxPkts, rx_packets);
|
||||
FALCON_STAT(efx, XgRxPktsOK, rx_good);
|
||||
FALCON_STAT(efx, XgRxBroadcastPkts, rx_broadcast);
|
||||
FALCON_STAT(efx, XgRxMulticastPkts, rx_multicast);
|
||||
FALCON_STAT(efx, XgRxUnicastPkts, rx_unicast);
|
||||
FALCON_STAT(efx, XgRxUndersizePkts, rx_lt64);
|
||||
FALCON_STAT(efx, XgRxOversizePkts, rx_gtjumbo);
|
||||
FALCON_STAT(efx, XgRxJabberPkts, rx_bad_gtjumbo);
|
||||
FALCON_STAT(efx, XgRxUndersizeFCSerrorPkts, rx_bad_lt64);
|
||||
FALCON_STAT(efx, XgRxDropEvents, rx_overflow);
|
||||
FALCON_STAT(efx, XgRxFCSerrorPkts, rx_bad);
|
||||
FALCON_STAT(efx, XgRxAlignError, rx_align_error);
|
||||
FALCON_STAT(efx, XgRxSymbolError, rx_symbol_error);
|
||||
FALCON_STAT(efx, XgRxInternalMACError, rx_internal_error);
|
||||
FALCON_STAT(efx, XgRxControlPkts, rx_control);
|
||||
FALCON_STAT(efx, XgRxPausePkts, rx_pause);
|
||||
FALCON_STAT(efx, XgRxPkts64Octets, rx_64);
|
||||
FALCON_STAT(efx, XgRxPkts65to127Octets, rx_65_to_127);
|
||||
FALCON_STAT(efx, XgRxPkts128to255Octets, rx_128_to_255);
|
||||
FALCON_STAT(efx, XgRxPkts256to511Octets, rx_256_to_511);
|
||||
FALCON_STAT(efx, XgRxPkts512to1023Octets, rx_512_to_1023);
|
||||
FALCON_STAT(efx, XgRxPkts1024to15xxOctets, rx_1024_to_15xx);
|
||||
FALCON_STAT(efx, XgRxPkts15xxtoMaxOctets, rx_15xx_to_jumbo);
|
||||
FALCON_STAT(efx, XgRxLengthError, rx_length_error);
|
||||
FALCON_STAT(efx, XgTxPkts, tx_packets);
|
||||
FALCON_STAT(efx, XgTxOctets, tx_bytes);
|
||||
FALCON_STAT(efx, XgTxMulticastPkts, tx_multicast);
|
||||
FALCON_STAT(efx, XgTxBroadcastPkts, tx_broadcast);
|
||||
FALCON_STAT(efx, XgTxUnicastPkts, tx_unicast);
|
||||
FALCON_STAT(efx, XgTxControlPkts, tx_control);
|
||||
FALCON_STAT(efx, XgTxPausePkts, tx_pause);
|
||||
FALCON_STAT(efx, XgTxPkts64Octets, tx_64);
|
||||
FALCON_STAT(efx, XgTxPkts65to127Octets, tx_65_to_127);
|
||||
FALCON_STAT(efx, XgTxPkts128to255Octets, tx_128_to_255);
|
||||
FALCON_STAT(efx, XgTxPkts256to511Octets, tx_256_to_511);
|
||||
FALCON_STAT(efx, XgTxPkts512to1023Octets, tx_512_to_1023);
|
||||
FALCON_STAT(efx, XgTxPkts1024to15xxOctets, tx_1024_to_15xx);
|
||||
FALCON_STAT(efx, XgTxPkts1519toMaxOctets, tx_15xx_to_jumbo);
|
||||
FALCON_STAT(efx, XgTxUndersizePkts, tx_lt64);
|
||||
FALCON_STAT(efx, XgTxOversizePkts, tx_gtjumbo);
|
||||
FALCON_STAT(efx, XgTxNonTcpUdpPkt, tx_non_tcpudp);
|
||||
FALCON_STAT(efx, XgTxMacSrcErrPkt, tx_mac_src_error);
|
||||
FALCON_STAT(efx, XgTxIpSrcErrPkt, tx_ip_src_error);
|
||||
|
||||
/* Update derived statistics */
|
||||
mac_stats->tx_good_bytes =
|
||||
(mac_stats->tx_bytes - mac_stats->tx_bad_bytes);
|
||||
mac_stats->rx_bad_bytes =
|
||||
(mac_stats->rx_bytes - mac_stats->rx_good_bytes);
|
||||
}
|
||||
|
||||
#define EFX_XAUI_RETRAIN_MAX 8
|
||||
|
||||
int falcon_check_xmac(struct efx_nic *efx)
|
||||
{
|
||||
unsigned xaui_link_ok;
|
||||
int rc;
|
||||
|
||||
falcon_mask_status_intr(efx, 0);
|
||||
xaui_link_ok = falcon_xaui_link_ok(efx);
|
||||
|
||||
if (EFX_WORKAROUND_5147(efx) && !xaui_link_ok)
|
||||
(void) falcon_reset_xaui(efx);
|
||||
|
||||
/* Call the PHY check_hw routine */
|
||||
rc = efx->phy_op->check_hw(efx);
|
||||
|
||||
/* Unmask interrupt if everything was (and still is) ok */
|
||||
if (xaui_link_ok && efx->link_up)
|
||||
falcon_mask_status_intr(efx, 1);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* Simulate a PHY event */
|
||||
void falcon_xmac_sim_phy_event(struct efx_nic *efx)
|
||||
{
|
||||
efx_qword_t phy_event;
|
||||
|
||||
EFX_POPULATE_QWORD_2(phy_event,
|
||||
EV_CODE, GLOBAL_EV_DECODE,
|
||||
XG_PHY_INTR, 1);
|
||||
falcon_generate_event(&efx->channel[0], &phy_event);
|
||||
}
|
||||
|
||||
int falcon_xmac_get_settings(struct efx_nic *efx, struct ethtool_cmd *ecmd)
|
||||
{
|
||||
mdio_clause45_get_settings(efx, ecmd);
|
||||
ecmd->transceiver = XCVR_INTERNAL;
|
||||
ecmd->phy_address = efx->mii.phy_id;
|
||||
ecmd->autoneg = AUTONEG_DISABLE;
|
||||
ecmd->duplex = DUPLEX_FULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int falcon_xmac_set_settings(struct efx_nic *efx, struct ethtool_cmd *ecmd)
|
||||
{
|
||||
if (ecmd->transceiver != XCVR_INTERNAL)
|
||||
return -EINVAL;
|
||||
if (ecmd->autoneg != AUTONEG_DISABLE)
|
||||
return -EINVAL;
|
||||
if (ecmd->duplex != DUPLEX_FULL)
|
||||
return -EINVAL;
|
||||
|
||||
return mdio_clause45_set_settings(efx, ecmd);
|
||||
}
|
||||
|
||||
|
||||
int falcon_xmac_set_pause(struct efx_nic *efx, enum efx_fc_type flow_control)
|
||||
{
|
||||
int reset;
|
||||
|
||||
if (flow_control & EFX_FC_AUTO) {
|
||||
EFX_LOG(efx, "10G does not support flow control "
|
||||
"autonegotiation\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if ((flow_control & EFX_FC_TX) && !(flow_control & EFX_FC_RX))
|
||||
return -EINVAL;
|
||||
|
||||
/* TX flow control may automatically turn itself off if the
|
||||
* link partner (intermittently) stops responding to pause
|
||||
* frames. There isn't any indication that this has happened,
|
||||
* so the best we do is leave it up to the user to spot this
|
||||
* and fix it be cycling transmit flow control on this end. */
|
||||
reset = ((flow_control & EFX_FC_TX) &&
|
||||
!(efx->flow_control & EFX_FC_TX));
|
||||
if (EFX_WORKAROUND_11482(efx) && reset) {
|
||||
if (FALCON_REV(efx) >= FALCON_REV_B0) {
|
||||
/* Recover by resetting the EM block */
|
||||
if (efx->link_up)
|
||||
falcon_drain_tx_fifo(efx);
|
||||
} else {
|
||||
/* Schedule a reset to recover */
|
||||
efx_schedule_reset(efx, RESET_TYPE_INVISIBLE);
|
||||
}
|
||||
}
|
||||
|
||||
efx->flow_control = flow_control;
|
||||
|
||||
return 0;
|
||||
}
|
195
drivers/net/sfc/gmii.h
Normal file
195
drivers/net/sfc/gmii.h
Normal file
@ -0,0 +1,195 @@
|
||||
/****************************************************************************
|
||||
* Driver for Solarflare Solarstorm network controllers and boards
|
||||
* Copyright 2005-2006 Fen Systems Ltd.
|
||||
* Copyright 2006 Solarflare Communications Inc.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 as published
|
||||
* by the Free Software Foundation, incorporated herein by reference.
|
||||
*/
|
||||
|
||||
#ifndef EFX_GMII_H
|
||||
#define EFX_GMII_H
|
||||
|
||||
/*
|
||||
* GMII interface
|
||||
*/
|
||||
|
||||
#include <linux/mii.h>
|
||||
|
||||
/* GMII registers, excluding registers already defined as MII
|
||||
* registers in mii.h
|
||||
*/
|
||||
#define GMII_IER 0x12 /* Interrupt enable register */
|
||||
#define GMII_ISR 0x13 /* Interrupt status register */
|
||||
|
||||
/* Interrupt enable register */
|
||||
#define IER_ANEG_ERR 0x8000 /* Bit 15 - autonegotiation error */
|
||||
#define IER_SPEED_CHG 0x4000 /* Bit 14 - speed changed */
|
||||
#define IER_DUPLEX_CHG 0x2000 /* Bit 13 - duplex changed */
|
||||
#define IER_PAGE_RCVD 0x1000 /* Bit 12 - page received */
|
||||
#define IER_ANEG_DONE 0x0800 /* Bit 11 - autonegotiation complete */
|
||||
#define IER_LINK_CHG 0x0400 /* Bit 10 - link status changed */
|
||||
#define IER_SYM_ERR 0x0200 /* Bit 9 - symbol error */
|
||||
#define IER_FALSE_CARRIER 0x0100 /* Bit 8 - false carrier */
|
||||
#define IER_FIFO_ERR 0x0080 /* Bit 7 - FIFO over/underflow */
|
||||
#define IER_MDIX_CHG 0x0040 /* Bit 6 - MDI crossover changed */
|
||||
#define IER_DOWNSHIFT 0x0020 /* Bit 5 - downshift */
|
||||
#define IER_ENERGY 0x0010 /* Bit 4 - energy detect */
|
||||
#define IER_DTE_POWER 0x0004 /* Bit 2 - DTE power detect */
|
||||
#define IER_POLARITY_CHG 0x0002 /* Bit 1 - polarity changed */
|
||||
#define IER_JABBER 0x0001 /* Bit 0 - jabber */
|
||||
|
||||
/* Interrupt status register */
|
||||
#define ISR_ANEG_ERR 0x8000 /* Bit 15 - autonegotiation error */
|
||||
#define ISR_SPEED_CHG 0x4000 /* Bit 14 - speed changed */
|
||||
#define ISR_DUPLEX_CHG 0x2000 /* Bit 13 - duplex changed */
|
||||
#define ISR_PAGE_RCVD 0x1000 /* Bit 12 - page received */
|
||||
#define ISR_ANEG_DONE 0x0800 /* Bit 11 - autonegotiation complete */
|
||||
#define ISR_LINK_CHG 0x0400 /* Bit 10 - link status changed */
|
||||
#define ISR_SYM_ERR 0x0200 /* Bit 9 - symbol error */
|
||||
#define ISR_FALSE_CARRIER 0x0100 /* Bit 8 - false carrier */
|
||||
#define ISR_FIFO_ERR 0x0080 /* Bit 7 - FIFO over/underflow */
|
||||
#define ISR_MDIX_CHG 0x0040 /* Bit 6 - MDI crossover changed */
|
||||
#define ISR_DOWNSHIFT 0x0020 /* Bit 5 - downshift */
|
||||
#define ISR_ENERGY 0x0010 /* Bit 4 - energy detect */
|
||||
#define ISR_DTE_POWER 0x0004 /* Bit 2 - DTE power detect */
|
||||
#define ISR_POLARITY_CHG 0x0002 /* Bit 1 - polarity changed */
|
||||
#define ISR_JABBER 0x0001 /* Bit 0 - jabber */
|
||||
|
||||
/* Logically extended advertisement register */
|
||||
#define GM_ADVERTISE_SLCT ADVERTISE_SLCT
|
||||
#define GM_ADVERTISE_CSMA ADVERTISE_CSMA
|
||||
#define GM_ADVERTISE_10HALF ADVERTISE_10HALF
|
||||
#define GM_ADVERTISE_1000XFULL ADVERTISE_1000XFULL
|
||||
#define GM_ADVERTISE_10FULL ADVERTISE_10FULL
|
||||
#define GM_ADVERTISE_1000XHALF ADVERTISE_1000XHALF
|
||||
#define GM_ADVERTISE_100HALF ADVERTISE_100HALF
|
||||
#define GM_ADVERTISE_1000XPAUSE ADVERTISE_1000XPAUSE
|
||||
#define GM_ADVERTISE_100FULL ADVERTISE_100FULL
|
||||
#define GM_ADVERTISE_1000XPSE_ASYM ADVERTISE_1000XPSE_ASYM
|
||||
#define GM_ADVERTISE_100BASE4 ADVERTISE_100BASE4
|
||||
#define GM_ADVERTISE_PAUSE_CAP ADVERTISE_PAUSE_CAP
|
||||
#define GM_ADVERTISE_PAUSE_ASYM ADVERTISE_PAUSE_ASYM
|
||||
#define GM_ADVERTISE_RESV ADVERTISE_RESV
|
||||
#define GM_ADVERTISE_RFAULT ADVERTISE_RFAULT
|
||||
#define GM_ADVERTISE_LPACK ADVERTISE_LPACK
|
||||
#define GM_ADVERTISE_NPAGE ADVERTISE_NPAGE
|
||||
#define GM_ADVERTISE_1000FULL (ADVERTISE_1000FULL << 8)
|
||||
#define GM_ADVERTISE_1000HALF (ADVERTISE_1000HALF << 8)
|
||||
#define GM_ADVERTISE_1000 (GM_ADVERTISE_1000FULL | \
|
||||
GM_ADVERTISE_1000HALF)
|
||||
#define GM_ADVERTISE_FULL (GM_ADVERTISE_1000FULL | \
|
||||
ADVERTISE_FULL)
|
||||
#define GM_ADVERTISE_ALL (GM_ADVERTISE_1000FULL | \
|
||||
GM_ADVERTISE_1000HALF | \
|
||||
ADVERTISE_ALL)
|
||||
|
||||
/* Logically extended link partner ability register */
|
||||
#define GM_LPA_SLCT LPA_SLCT
|
||||
#define GM_LPA_10HALF LPA_10HALF
|
||||
#define GM_LPA_1000XFULL LPA_1000XFULL
|
||||
#define GM_LPA_10FULL LPA_10FULL
|
||||
#define GM_LPA_1000XHALF LPA_1000XHALF
|
||||
#define GM_LPA_100HALF LPA_100HALF
|
||||
#define GM_LPA_1000XPAUSE LPA_1000XPAUSE
|
||||
#define GM_LPA_100FULL LPA_100FULL
|
||||
#define GM_LPA_1000XPAUSE_ASYM LPA_1000XPAUSE_ASYM
|
||||
#define GM_LPA_100BASE4 LPA_100BASE4
|
||||
#define GM_LPA_PAUSE_CAP LPA_PAUSE_CAP
|
||||
#define GM_LPA_PAUSE_ASYM LPA_PAUSE_ASYM
|
||||
#define GM_LPA_RESV LPA_RESV
|
||||
#define GM_LPA_RFAULT LPA_RFAULT
|
||||
#define GM_LPA_LPACK LPA_LPACK
|
||||
#define GM_LPA_NPAGE LPA_NPAGE
|
||||
#define GM_LPA_1000FULL (LPA_1000FULL << 6)
|
||||
#define GM_LPA_1000HALF (LPA_1000HALF << 6)
|
||||
#define GM_LPA_10000FULL 0x00040000
|
||||
#define GM_LPA_10000HALF 0x00080000
|
||||
#define GM_LPA_DUPLEX (GM_LPA_1000FULL | GM_LPA_10000FULL \
|
||||
| LPA_DUPLEX)
|
||||
#define GM_LPA_10 (LPA_10FULL | LPA_10HALF)
|
||||
#define GM_LPA_100 LPA_100
|
||||
#define GM_LPA_1000 (GM_LPA_1000FULL | GM_LPA_1000HALF)
|
||||
#define GM_LPA_10000 (GM_LPA_10000FULL | GM_LPA_10000HALF)
|
||||
|
||||
/* Retrieve GMII autonegotiation advertised abilities
|
||||
*
|
||||
* The MII advertisment register (MII_ADVERTISE) is logically extended
|
||||
* to include advertisement bits ADVERTISE_1000FULL and
|
||||
* ADVERTISE_1000HALF from MII_CTRL1000. The result can be tested
|
||||
* against the GM_ADVERTISE_xxx constants.
|
||||
*/
|
||||
static inline unsigned int gmii_advertised(struct mii_if_info *gmii)
|
||||
{
|
||||
unsigned int advertise;
|
||||
unsigned int ctrl1000;
|
||||
|
||||
advertise = gmii->mdio_read(gmii->dev, gmii->phy_id, MII_ADVERTISE);
|
||||
ctrl1000 = gmii->mdio_read(gmii->dev, gmii->phy_id, MII_CTRL1000);
|
||||
return (((ctrl1000 << 8) & GM_ADVERTISE_1000) | advertise);
|
||||
}
|
||||
|
||||
/* Retrieve GMII autonegotiation link partner abilities
|
||||
*
|
||||
* The MII link partner ability register (MII_LPA) is logically
|
||||
* extended by adding bits LPA_1000HALF and LPA_1000FULL from
|
||||
* MII_STAT1000. The result can be tested against the GM_LPA_xxx
|
||||
* constants.
|
||||
*/
|
||||
static inline unsigned int gmii_lpa(struct mii_if_info *gmii)
|
||||
{
|
||||
unsigned int lpa;
|
||||
unsigned int stat1000;
|
||||
|
||||
lpa = gmii->mdio_read(gmii->dev, gmii->phy_id, MII_LPA);
|
||||
stat1000 = gmii->mdio_read(gmii->dev, gmii->phy_id, MII_STAT1000);
|
||||
return (((stat1000 << 6) & GM_LPA_1000) | lpa);
|
||||
}
|
||||
|
||||
/* Calculate GMII autonegotiated link technology
|
||||
*
|
||||
* "negotiated" should be the result of gmii_advertised() logically
|
||||
* ANDed with the result of gmii_lpa().
|
||||
*
|
||||
* "tech" will be negotiated with the unused bits masked out. For
|
||||
* example, if both ends of the link are capable of both
|
||||
* GM_LPA_1000FULL and GM_LPA_100FULL, GM_LPA_100FULL will be masked
|
||||
* out.
|
||||
*/
|
||||
static inline unsigned int gmii_nway_result(unsigned int negotiated)
|
||||
{
|
||||
unsigned int other_bits;
|
||||
|
||||
/* Mask out the speed and duplexity bits */
|
||||
other_bits = negotiated & ~(GM_LPA_10 | GM_LPA_100 | GM_LPA_1000);
|
||||
|
||||
if (negotiated & GM_LPA_1000FULL)
|
||||
return (other_bits | GM_LPA_1000FULL);
|
||||
else if (negotiated & GM_LPA_1000HALF)
|
||||
return (other_bits | GM_LPA_1000HALF);
|
||||
else
|
||||
return (other_bits | mii_nway_result(negotiated));
|
||||
}
|
||||
|
||||
/* Calculate GMII non-autonegotiated link technology
|
||||
*
|
||||
* This provides an equivalent to gmii_nway_result for the case when
|
||||
* autonegotiation is disabled.
|
||||
*/
|
||||
static inline unsigned int gmii_forced_result(unsigned int bmcr)
|
||||
{
|
||||
unsigned int result;
|
||||
int full_duplex;
|
||||
|
||||
full_duplex = bmcr & BMCR_FULLDPLX;
|
||||
if (bmcr & BMCR_SPEED1000)
|
||||
result = full_duplex ? GM_LPA_1000FULL : GM_LPA_1000HALF;
|
||||
else if (bmcr & BMCR_SPEED100)
|
||||
result = full_duplex ? GM_LPA_100FULL : GM_LPA_100HALF;
|
||||
else
|
||||
result = full_duplex ? GM_LPA_10FULL : GM_LPA_10HALF;
|
||||
return result;
|
||||
}
|
||||
|
||||
#endif /* EFX_GMII_H */
|
381
drivers/net/sfc/i2c-direct.c
Normal file
381
drivers/net/sfc/i2c-direct.c
Normal file
@ -0,0 +1,381 @@
|
||||
/****************************************************************************
|
||||
* Driver for Solarflare Solarstorm network controllers and boards
|
||||
* Copyright 2005 Fen Systems Ltd.
|
||||
* Copyright 2006-2008 Solarflare Communications Inc.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 as published
|
||||
* by the Free Software Foundation, incorporated herein by reference.
|
||||
*/
|
||||
|
||||
#include <linux/delay.h>
|
||||
#include "net_driver.h"
|
||||
#include "i2c-direct.h"
|
||||
|
||||
/*
|
||||
* I2C data (SDA) and clock (SCL) line read/writes with appropriate
|
||||
* delays.
|
||||
*/
|
||||
|
||||
static inline void setsda(struct efx_i2c_interface *i2c, int state)
|
||||
{
|
||||
udelay(i2c->op->udelay);
|
||||
i2c->sda = state;
|
||||
i2c->op->setsda(i2c);
|
||||
udelay(i2c->op->udelay);
|
||||
}
|
||||
|
||||
static inline void setscl(struct efx_i2c_interface *i2c, int state)
|
||||
{
|
||||
udelay(i2c->op->udelay);
|
||||
i2c->scl = state;
|
||||
i2c->op->setscl(i2c);
|
||||
udelay(i2c->op->udelay);
|
||||
}
|
||||
|
||||
static inline int getsda(struct efx_i2c_interface *i2c)
|
||||
{
|
||||
int sda;
|
||||
|
||||
udelay(i2c->op->udelay);
|
||||
sda = i2c->op->getsda(i2c);
|
||||
udelay(i2c->op->udelay);
|
||||
return sda;
|
||||
}
|
||||
|
||||
static inline int getscl(struct efx_i2c_interface *i2c)
|
||||
{
|
||||
int scl;
|
||||
|
||||
udelay(i2c->op->udelay);
|
||||
scl = i2c->op->getscl(i2c);
|
||||
udelay(i2c->op->udelay);
|
||||
return scl;
|
||||
}
|
||||
|
||||
/*
|
||||
* I2C low-level protocol operations
|
||||
*
|
||||
*/
|
||||
|
||||
static inline void i2c_release(struct efx_i2c_interface *i2c)
|
||||
{
|
||||
EFX_WARN_ON_PARANOID(!i2c->scl);
|
||||
EFX_WARN_ON_PARANOID(!i2c->sda);
|
||||
/* Devices may time out if operations do not end */
|
||||
setscl(i2c, 1);
|
||||
setsda(i2c, 1);
|
||||
EFX_BUG_ON_PARANOID(getsda(i2c) != 1);
|
||||
EFX_BUG_ON_PARANOID(getscl(i2c) != 1);
|
||||
}
|
||||
|
||||
static inline void i2c_start(struct efx_i2c_interface *i2c)
|
||||
{
|
||||
/* We may be restarting immediately after a {send,recv}_bit,
|
||||
* so SCL will not necessarily already be high.
|
||||
*/
|
||||
EFX_WARN_ON_PARANOID(!i2c->sda);
|
||||
setscl(i2c, 1);
|
||||
setsda(i2c, 0);
|
||||
setscl(i2c, 0);
|
||||
setsda(i2c, 1);
|
||||
}
|
||||
|
||||
static inline void i2c_send_bit(struct efx_i2c_interface *i2c, int bit)
|
||||
{
|
||||
EFX_WARN_ON_PARANOID(i2c->scl != 0);
|
||||
setsda(i2c, bit);
|
||||
setscl(i2c, 1);
|
||||
setscl(i2c, 0);
|
||||
setsda(i2c, 1);
|
||||
}
|
||||
|
||||
static inline int i2c_recv_bit(struct efx_i2c_interface *i2c)
|
||||
{
|
||||
int bit;
|
||||
|
||||
EFX_WARN_ON_PARANOID(i2c->scl != 0);
|
||||
EFX_WARN_ON_PARANOID(!i2c->sda);
|
||||
setscl(i2c, 1);
|
||||
bit = getsda(i2c);
|
||||
setscl(i2c, 0);
|
||||
return bit;
|
||||
}
|
||||
|
||||
static inline void i2c_stop(struct efx_i2c_interface *i2c)
|
||||
{
|
||||
EFX_WARN_ON_PARANOID(i2c->scl != 0);
|
||||
setsda(i2c, 0);
|
||||
setscl(i2c, 1);
|
||||
setsda(i2c, 1);
|
||||
}
|
||||
|
||||
/*
|
||||
* I2C mid-level protocol operations
|
||||
*
|
||||
*/
|
||||
|
||||
/* Sends a byte via the I2C bus and checks for an acknowledgement from
|
||||
* the slave device.
|
||||
*/
|
||||
static int i2c_send_byte(struct efx_i2c_interface *i2c, u8 byte)
|
||||
{
|
||||
int i;
|
||||
|
||||
/* Send byte */
|
||||
for (i = 0; i < 8; i++) {
|
||||
i2c_send_bit(i2c, !!(byte & 0x80));
|
||||
byte <<= 1;
|
||||
}
|
||||
|
||||
/* Check for acknowledgement from slave */
|
||||
return (i2c_recv_bit(i2c) == 0 ? 0 : -EIO);
|
||||
}
|
||||
|
||||
/* Receives a byte via the I2C bus and sends ACK/NACK to the slave device. */
|
||||
static u8 i2c_recv_byte(struct efx_i2c_interface *i2c, int ack)
|
||||
{
|
||||
u8 value = 0;
|
||||
int i;
|
||||
|
||||
/* Receive byte */
|
||||
for (i = 0; i < 8; i++)
|
||||
value = (value << 1) | i2c_recv_bit(i2c);
|
||||
|
||||
/* Send ACK/NACK */
|
||||
i2c_send_bit(i2c, (ack ? 0 : 1));
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
/* Calculate command byte for a read operation */
|
||||
static inline u8 i2c_read_cmd(u8 device_id)
|
||||
{
|
||||
return ((device_id << 1) | 1);
|
||||
}
|
||||
|
||||
/* Calculate command byte for a write operation */
|
||||
static inline u8 i2c_write_cmd(u8 device_id)
|
||||
{
|
||||
return ((device_id << 1) | 0);
|
||||
}
|
||||
|
||||
int efx_i2c_check_presence(struct efx_i2c_interface *i2c, u8 device_id)
|
||||
{
|
||||
int rc;
|
||||
|
||||
/* If someone is driving the bus low we just give up. */
|
||||
if (getsda(i2c) == 0 || getscl(i2c) == 0) {
|
||||
EFX_ERR(i2c->efx, "%s someone is holding the I2C bus low."
|
||||
" Giving up.\n", __func__);
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
/* Pretend to initiate a device write */
|
||||
i2c_start(i2c);
|
||||
rc = i2c_send_byte(i2c, i2c_write_cmd(device_id));
|
||||
if (rc)
|
||||
goto out;
|
||||
|
||||
out:
|
||||
i2c_stop(i2c);
|
||||
i2c_release(i2c);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* This performs a fast read of one or more consecutive bytes from an
|
||||
* I2C device. Not all devices support consecutive reads of more than
|
||||
* one byte; for these devices use efx_i2c_read() instead.
|
||||
*/
|
||||
int efx_i2c_fast_read(struct efx_i2c_interface *i2c,
|
||||
u8 device_id, u8 offset, u8 *data, unsigned int len)
|
||||
{
|
||||
int i;
|
||||
int rc;
|
||||
|
||||
EFX_WARN_ON_PARANOID(getsda(i2c) != 1);
|
||||
EFX_WARN_ON_PARANOID(getscl(i2c) != 1);
|
||||
EFX_WARN_ON_PARANOID(data == NULL);
|
||||
EFX_WARN_ON_PARANOID(len < 1);
|
||||
|
||||
/* Select device and starting offset */
|
||||
i2c_start(i2c);
|
||||
rc = i2c_send_byte(i2c, i2c_write_cmd(device_id));
|
||||
if (rc)
|
||||
goto out;
|
||||
rc = i2c_send_byte(i2c, offset);
|
||||
if (rc)
|
||||
goto out;
|
||||
|
||||
/* Read data from device */
|
||||
i2c_start(i2c);
|
||||
rc = i2c_send_byte(i2c, i2c_read_cmd(device_id));
|
||||
if (rc)
|
||||
goto out;
|
||||
for (i = 0; i < (len - 1); i++)
|
||||
/* Read and acknowledge all but the last byte */
|
||||
data[i] = i2c_recv_byte(i2c, 1);
|
||||
/* Read last byte with no acknowledgement */
|
||||
data[i] = i2c_recv_byte(i2c, 0);
|
||||
|
||||
out:
|
||||
i2c_stop(i2c);
|
||||
i2c_release(i2c);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* This performs a fast write of one or more consecutive bytes to an
|
||||
* I2C device. Not all devices support consecutive writes of more
|
||||
* than one byte; for these devices use efx_i2c_write() instead.
|
||||
*/
|
||||
int efx_i2c_fast_write(struct efx_i2c_interface *i2c,
|
||||
u8 device_id, u8 offset,
|
||||
const u8 *data, unsigned int len)
|
||||
{
|
||||
int i;
|
||||
int rc;
|
||||
|
||||
EFX_WARN_ON_PARANOID(getsda(i2c) != 1);
|
||||
EFX_WARN_ON_PARANOID(getscl(i2c) != 1);
|
||||
EFX_WARN_ON_PARANOID(len < 1);
|
||||
|
||||
/* Select device and starting offset */
|
||||
i2c_start(i2c);
|
||||
rc = i2c_send_byte(i2c, i2c_write_cmd(device_id));
|
||||
if (rc)
|
||||
goto out;
|
||||
rc = i2c_send_byte(i2c, offset);
|
||||
if (rc)
|
||||
goto out;
|
||||
|
||||
/* Write data to device */
|
||||
for (i = 0; i < len; i++) {
|
||||
rc = i2c_send_byte(i2c, data[i]);
|
||||
if (rc)
|
||||
goto out;
|
||||
}
|
||||
|
||||
out:
|
||||
i2c_stop(i2c);
|
||||
i2c_release(i2c);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* I2C byte-by-byte read */
|
||||
int efx_i2c_read(struct efx_i2c_interface *i2c,
|
||||
u8 device_id, u8 offset, u8 *data, unsigned int len)
|
||||
{
|
||||
int rc;
|
||||
|
||||
/* i2c_fast_read with length 1 is a single byte read */
|
||||
for (; len > 0; offset++, data++, len--) {
|
||||
rc = efx_i2c_fast_read(i2c, device_id, offset, data, 1);
|
||||
if (rc)
|
||||
return rc;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* I2C byte-by-byte write */
|
||||
int efx_i2c_write(struct efx_i2c_interface *i2c,
|
||||
u8 device_id, u8 offset, const u8 *data, unsigned int len)
|
||||
{
|
||||
int rc;
|
||||
|
||||
/* i2c_fast_write with length 1 is a single byte write */
|
||||
for (; len > 0; offset++, data++, len--) {
|
||||
rc = efx_i2c_fast_write(i2c, device_id, offset, data, 1);
|
||||
if (rc)
|
||||
return rc;
|
||||
mdelay(i2c->op->mdelay);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* This is just a slightly neater wrapper round efx_i2c_fast_write
|
||||
* in the case where the target doesn't take an offset
|
||||
*/
|
||||
int efx_i2c_send_bytes(struct efx_i2c_interface *i2c,
|
||||
u8 device_id, const u8 *data, unsigned int len)
|
||||
{
|
||||
return efx_i2c_fast_write(i2c, device_id, data[0], data + 1, len - 1);
|
||||
}
|
||||
|
||||
/* I2C receiving of bytes - does not send an offset byte */
|
||||
int efx_i2c_recv_bytes(struct efx_i2c_interface *i2c, u8 device_id,
|
||||
u8 *bytes, unsigned int len)
|
||||
{
|
||||
int i;
|
||||
int rc;
|
||||
|
||||
EFX_WARN_ON_PARANOID(getsda(i2c) != 1);
|
||||
EFX_WARN_ON_PARANOID(getscl(i2c) != 1);
|
||||
EFX_WARN_ON_PARANOID(len < 1);
|
||||
|
||||
/* Select device */
|
||||
i2c_start(i2c);
|
||||
|
||||
/* Read data from device */
|
||||
rc = i2c_send_byte(i2c, i2c_read_cmd(device_id));
|
||||
if (rc)
|
||||
goto out;
|
||||
|
||||
for (i = 0; i < (len - 1); i++)
|
||||
/* Read and acknowledge all but the last byte */
|
||||
bytes[i] = i2c_recv_byte(i2c, 1);
|
||||
/* Read last byte with no acknowledgement */
|
||||
bytes[i] = i2c_recv_byte(i2c, 0);
|
||||
|
||||
out:
|
||||
i2c_stop(i2c);
|
||||
i2c_release(i2c);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* SMBus and some I2C devices will time out if the I2C clock is
|
||||
* held low for too long. This is most likely to happen in virtualised
|
||||
* systems (when the entire domain is descheduled) but could in
|
||||
* principle happen due to preemption on any busy system (and given the
|
||||
* potential length of an I2C operation turning preemption off is not
|
||||
* a sensible option). The following functions deal with the failure by
|
||||
* retrying up to a fixed number of times.
|
||||
*/
|
||||
|
||||
#define I2C_MAX_RETRIES (10)
|
||||
|
||||
/* The timeout problem will result in -EIO. If the wrapped function
|
||||
* returns any other error, pass this up and do not retry. */
|
||||
#define RETRY_WRAPPER(_f) \
|
||||
int retries = I2C_MAX_RETRIES; \
|
||||
int rc; \
|
||||
while (retries) { \
|
||||
rc = _f; \
|
||||
if (rc != -EIO) \
|
||||
return rc; \
|
||||
retries--; \
|
||||
} \
|
||||
return rc; \
|
||||
|
||||
int efx_i2c_check_presence_retry(struct efx_i2c_interface *i2c, u8 device_id)
|
||||
{
|
||||
RETRY_WRAPPER(efx_i2c_check_presence(i2c, device_id))
|
||||
}
|
||||
|
||||
int efx_i2c_read_retry(struct efx_i2c_interface *i2c,
|
||||
u8 device_id, u8 offset, u8 *data, unsigned int len)
|
||||
{
|
||||
RETRY_WRAPPER(efx_i2c_read(i2c, device_id, offset, data, len))
|
||||
}
|
||||
|
||||
int efx_i2c_write_retry(struct efx_i2c_interface *i2c,
|
||||
u8 device_id, u8 offset, const u8 *data, unsigned int len)
|
||||
{
|
||||
RETRY_WRAPPER(efx_i2c_write(i2c, device_id, offset, data, len))
|
||||
}
|
91
drivers/net/sfc/i2c-direct.h
Normal file
91
drivers/net/sfc/i2c-direct.h
Normal file
@ -0,0 +1,91 @@
|
||||
/****************************************************************************
|
||||
* Driver for Solarflare Solarstorm network controllers and boards
|
||||
* Copyright 2005 Fen Systems Ltd.
|
||||
* Copyright 2006 Solarflare Communications Inc.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 as published
|
||||
* by the Free Software Foundation, incorporated herein by reference.
|
||||
*/
|
||||
|
||||
#ifndef EFX_I2C_DIRECT_H
|
||||
#define EFX_I2C_DIRECT_H
|
||||
|
||||
#include "net_driver.h"
|
||||
|
||||
/*
|
||||
* Direct control of an I2C bus
|
||||
*/
|
||||
|
||||
struct efx_i2c_interface;
|
||||
|
||||
/**
|
||||
* struct efx_i2c_bit_operations - I2C bus direct control methods
|
||||
*
|
||||
* I2C bus direct control methods.
|
||||
*
|
||||
* @setsda: Set state of SDA line
|
||||
* @setscl: Set state of SCL line
|
||||
* @getsda: Get state of SDA line
|
||||
* @getscl: Get state of SCL line
|
||||
* @udelay: Delay between each bit operation
|
||||
* @mdelay: Delay between each byte write
|
||||
*/
|
||||
struct efx_i2c_bit_operations {
|
||||
void (*setsda) (struct efx_i2c_interface *i2c);
|
||||
void (*setscl) (struct efx_i2c_interface *i2c);
|
||||
int (*getsda) (struct efx_i2c_interface *i2c);
|
||||
int (*getscl) (struct efx_i2c_interface *i2c);
|
||||
unsigned int udelay;
|
||||
unsigned int mdelay;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct efx_i2c_interface - an I2C interface
|
||||
*
|
||||
* An I2C interface.
|
||||
*
|
||||
* @efx: Attached Efx NIC
|
||||
* @op: I2C bus control methods
|
||||
* @sda: Current output state of SDA line
|
||||
* @scl: Current output state of SCL line
|
||||
*/
|
||||
struct efx_i2c_interface {
|
||||
struct efx_nic *efx;
|
||||
struct efx_i2c_bit_operations *op;
|
||||
unsigned int sda:1;
|
||||
unsigned int scl:1;
|
||||
};
|
||||
|
||||
extern int efx_i2c_check_presence(struct efx_i2c_interface *i2c, u8 device_id);
|
||||
extern int efx_i2c_fast_read(struct efx_i2c_interface *i2c,
|
||||
u8 device_id, u8 offset,
|
||||
u8 *data, unsigned int len);
|
||||
extern int efx_i2c_fast_write(struct efx_i2c_interface *i2c,
|
||||
u8 device_id, u8 offset,
|
||||
const u8 *data, unsigned int len);
|
||||
extern int efx_i2c_read(struct efx_i2c_interface *i2c,
|
||||
u8 device_id, u8 offset, u8 *data, unsigned int len);
|
||||
extern int efx_i2c_write(struct efx_i2c_interface *i2c,
|
||||
u8 device_id, u8 offset,
|
||||
const u8 *data, unsigned int len);
|
||||
|
||||
extern int efx_i2c_send_bytes(struct efx_i2c_interface *i2c, u8 device_id,
|
||||
const u8 *bytes, unsigned int len);
|
||||
|
||||
extern int efx_i2c_recv_bytes(struct efx_i2c_interface *i2c, u8 device_id,
|
||||
u8 *bytes, unsigned int len);
|
||||
|
||||
|
||||
/* Versions of the API that retry on failure. */
|
||||
extern int efx_i2c_check_presence_retry(struct efx_i2c_interface *i2c,
|
||||
u8 device_id);
|
||||
|
||||
extern int efx_i2c_read_retry(struct efx_i2c_interface *i2c,
|
||||
u8 device_id, u8 offset, u8 *data, unsigned int len);
|
||||
|
||||
extern int efx_i2c_write_retry(struct efx_i2c_interface *i2c,
|
||||
u8 device_id, u8 offset,
|
||||
const u8 *data, unsigned int len);
|
||||
|
||||
#endif /* EFX_I2C_DIRECT_H */
|
33
drivers/net/sfc/mac.h
Normal file
33
drivers/net/sfc/mac.h
Normal file
@ -0,0 +1,33 @@
|
||||
/****************************************************************************
|
||||
* Driver for Solarflare Solarstorm network controllers and boards
|
||||
* Copyright 2005-2006 Fen Systems Ltd.
|
||||
* Copyright 2006-2007 Solarflare Communications Inc.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 as published
|
||||
* by the Free Software Foundation, incorporated herein by reference.
|
||||
*/
|
||||
|
||||
#ifndef EFX_MAC_H
|
||||
#define EFX_MAC_H
|
||||
|
||||
#include "net_driver.h"
|
||||
|
||||
extern void falcon_xmac_writel(struct efx_nic *efx,
|
||||
efx_dword_t *value, unsigned int mac_reg);
|
||||
extern void falcon_xmac_readl(struct efx_nic *efx,
|
||||
efx_dword_t *value, unsigned int mac_reg);
|
||||
extern int falcon_init_xmac(struct efx_nic *efx);
|
||||
extern void falcon_reconfigure_xmac(struct efx_nic *efx);
|
||||
extern void falcon_update_stats_xmac(struct efx_nic *efx);
|
||||
extern void falcon_fini_xmac(struct efx_nic *efx);
|
||||
extern int falcon_check_xmac(struct efx_nic *efx);
|
||||
extern void falcon_xmac_sim_phy_event(struct efx_nic *efx);
|
||||
extern int falcon_xmac_get_settings(struct efx_nic *efx,
|
||||
struct ethtool_cmd *ecmd);
|
||||
extern int falcon_xmac_set_settings(struct efx_nic *efx,
|
||||
struct ethtool_cmd *ecmd);
|
||||
extern int falcon_xmac_set_pause(struct efx_nic *efx,
|
||||
enum efx_fc_type pause_params);
|
||||
|
||||
#endif
|
282
drivers/net/sfc/mdio_10g.c
Normal file
282
drivers/net/sfc/mdio_10g.c
Normal file
@ -0,0 +1,282 @@
|
||||
/****************************************************************************
|
||||
* Driver for Solarflare Solarstorm network controllers and boards
|
||||
* Copyright 2006-2008 Solarflare Communications Inc.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 as published
|
||||
* by the Free Software Foundation, incorporated herein by reference.
|
||||
*/
|
||||
/*
|
||||
* Useful functions for working with MDIO clause 45 PHYs
|
||||
*/
|
||||
#include <linux/types.h>
|
||||
#include <linux/ethtool.h>
|
||||
#include <linux/delay.h>
|
||||
#include "net_driver.h"
|
||||
#include "mdio_10g.h"
|
||||
#include "boards.h"
|
||||
|
||||
int mdio_clause45_reset_mmd(struct efx_nic *port, int mmd,
|
||||
int spins, int spintime)
|
||||
{
|
||||
u32 ctrl;
|
||||
int phy_id = port->mii.phy_id;
|
||||
|
||||
/* Catch callers passing values in the wrong units (or just silly) */
|
||||
EFX_BUG_ON_PARANOID(spins * spintime >= 5000);
|
||||
|
||||
mdio_clause45_write(port, phy_id, mmd, MDIO_MMDREG_CTRL1,
|
||||
(1 << MDIO_MMDREG_CTRL1_RESET_LBN));
|
||||
/* Wait for the reset bit to clear. */
|
||||
do {
|
||||
msleep(spintime);
|
||||
ctrl = mdio_clause45_read(port, phy_id, mmd, MDIO_MMDREG_CTRL1);
|
||||
spins--;
|
||||
|
||||
} while (spins && (ctrl & (1 << MDIO_MMDREG_CTRL1_RESET_LBN)));
|
||||
|
||||
return spins ? spins : -ETIMEDOUT;
|
||||
}
|
||||
|
||||
static int mdio_clause45_check_mmd(struct efx_nic *efx, int mmd,
|
||||
int fault_fatal)
|
||||
{
|
||||
int status;
|
||||
int phy_id = efx->mii.phy_id;
|
||||
|
||||
/* Read MMD STATUS2 to check it is responding. */
|
||||
status = mdio_clause45_read(efx, phy_id, mmd, MDIO_MMDREG_STAT2);
|
||||
if (((status >> MDIO_MMDREG_STAT2_PRESENT_LBN) &
|
||||
((1 << MDIO_MMDREG_STAT2_PRESENT_WIDTH) - 1)) !=
|
||||
MDIO_MMDREG_STAT2_PRESENT_VAL) {
|
||||
EFX_ERR(efx, "PHY MMD %d not responding.\n", mmd);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
/* Read MMD STATUS 1 to check for fault. */
|
||||
status = mdio_clause45_read(efx, phy_id, mmd, MDIO_MMDREG_STAT1);
|
||||
if ((status & (1 << MDIO_MMDREG_STAT1_FAULT_LBN)) != 0) {
|
||||
if (fault_fatal) {
|
||||
EFX_ERR(efx, "PHY MMD %d reporting fatal"
|
||||
" fault: status %x\n", mmd, status);
|
||||
return -EIO;
|
||||
} else {
|
||||
EFX_LOG(efx, "PHY MMD %d reporting status"
|
||||
" %x (expected)\n", mmd, status);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* This ought to be ridiculous overkill. We expect it to fail rarely */
|
||||
#define MDIO45_RESET_TIME 1000 /* ms */
|
||||
#define MDIO45_RESET_ITERS 100
|
||||
|
||||
int mdio_clause45_wait_reset_mmds(struct efx_nic *efx,
|
||||
unsigned int mmd_mask)
|
||||
{
|
||||
const int spintime = MDIO45_RESET_TIME / MDIO45_RESET_ITERS;
|
||||
int tries = MDIO45_RESET_ITERS;
|
||||
int rc = 0;
|
||||
int in_reset;
|
||||
|
||||
while (tries) {
|
||||
int mask = mmd_mask;
|
||||
int mmd = 0;
|
||||
int stat;
|
||||
in_reset = 0;
|
||||
while (mask) {
|
||||
if (mask & 1) {
|
||||
stat = mdio_clause45_read(efx,
|
||||
efx->mii.phy_id,
|
||||
mmd,
|
||||
MDIO_MMDREG_CTRL1);
|
||||
if (stat < 0) {
|
||||
EFX_ERR(efx, "failed to read status of"
|
||||
" MMD %d\n", mmd);
|
||||
return -EIO;
|
||||
}
|
||||
if (stat & (1 << MDIO_MMDREG_CTRL1_RESET_LBN))
|
||||
in_reset |= (1 << mmd);
|
||||
}
|
||||
mask = mask >> 1;
|
||||
mmd++;
|
||||
}
|
||||
if (!in_reset)
|
||||
break;
|
||||
tries--;
|
||||
msleep(spintime);
|
||||
}
|
||||
if (in_reset != 0) {
|
||||
EFX_ERR(efx, "not all MMDs came out of reset in time."
|
||||
" MMDs still in reset: %x\n", in_reset);
|
||||
rc = -ETIMEDOUT;
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
int mdio_clause45_check_mmds(struct efx_nic *efx,
|
||||
unsigned int mmd_mask, unsigned int fatal_mask)
|
||||
{
|
||||
int devices, mmd = 0;
|
||||
int probe_mmd;
|
||||
|
||||
/* Historically we have probed the PHYXS to find out what devices are
|
||||
* present,but that doesn't work so well if the PHYXS isn't expected
|
||||
* to exist, if so just find the first item in the list supplied. */
|
||||
probe_mmd = (mmd_mask & MDIO_MMDREG_DEVS0_PHYXS) ? MDIO_MMD_PHYXS :
|
||||
__ffs(mmd_mask);
|
||||
devices = mdio_clause45_read(efx, efx->mii.phy_id,
|
||||
probe_mmd, MDIO_MMDREG_DEVS0);
|
||||
|
||||
/* Check all the expected MMDs are present */
|
||||
if (devices < 0) {
|
||||
EFX_ERR(efx, "failed to read devices present\n");
|
||||
return -EIO;
|
||||
}
|
||||
if ((devices & mmd_mask) != mmd_mask) {
|
||||
EFX_ERR(efx, "required MMDs not present: got %x, "
|
||||
"wanted %x\n", devices, mmd_mask);
|
||||
return -ENODEV;
|
||||
}
|
||||
EFX_TRACE(efx, "Devices present: %x\n", devices);
|
||||
|
||||
/* Check all required MMDs are responding and happy. */
|
||||
while (mmd_mask) {
|
||||
if (mmd_mask & 1) {
|
||||
int fault_fatal = fatal_mask & 1;
|
||||
if (mdio_clause45_check_mmd(efx, mmd, fault_fatal))
|
||||
return -EIO;
|
||||
}
|
||||
mmd_mask = mmd_mask >> 1;
|
||||
fatal_mask = fatal_mask >> 1;
|
||||
mmd++;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int mdio_clause45_links_ok(struct efx_nic *efx, unsigned int mmd_mask)
|
||||
{
|
||||
int phy_id = efx->mii.phy_id;
|
||||
int status;
|
||||
int ok = 1;
|
||||
int mmd = 0;
|
||||
int good;
|
||||
|
||||
while (mmd_mask) {
|
||||
if (mmd_mask & 1) {
|
||||
/* Double reads because link state is latched, and a
|
||||
* read moves the current state into the register */
|
||||
status = mdio_clause45_read(efx, phy_id,
|
||||
mmd, MDIO_MMDREG_STAT1);
|
||||
status = mdio_clause45_read(efx, phy_id,
|
||||
mmd, MDIO_MMDREG_STAT1);
|
||||
|
||||
good = status & (1 << MDIO_MMDREG_STAT1_LINK_LBN);
|
||||
ok = ok && good;
|
||||
}
|
||||
mmd_mask = (mmd_mask >> 1);
|
||||
mmd++;
|
||||
}
|
||||
return ok;
|
||||
}
|
||||
|
||||
/**
|
||||
* mdio_clause45_get_settings - Read (some of) the PHY settings over MDIO.
|
||||
* @efx: Efx NIC
|
||||
* @ecmd: Buffer for settings
|
||||
*
|
||||
* On return the 'port', 'speed', 'supported' and 'advertising' fields of
|
||||
* ecmd have been filled out based on the PMA type.
|
||||
*/
|
||||
void mdio_clause45_get_settings(struct efx_nic *efx,
|
||||
struct ethtool_cmd *ecmd)
|
||||
{
|
||||
int pma_type;
|
||||
|
||||
/* If no PMA is present we are presumably talking something XAUI-ish
|
||||
* like CX4. Which we report as FIBRE (see below) */
|
||||
if ((efx->phy_op->mmds & DEV_PRESENT_BIT(MDIO_MMD_PMAPMD)) == 0) {
|
||||
ecmd->speed = SPEED_10000;
|
||||
ecmd->port = PORT_FIBRE;
|
||||
ecmd->supported = SUPPORTED_FIBRE;
|
||||
ecmd->advertising = ADVERTISED_FIBRE;
|
||||
return;
|
||||
}
|
||||
|
||||
pma_type = mdio_clause45_read(efx, efx->mii.phy_id,
|
||||
MDIO_MMD_PMAPMD, MDIO_MMDREG_CTRL2);
|
||||
pma_type &= MDIO_PMAPMD_CTRL2_TYPE_MASK;
|
||||
|
||||
switch (pma_type) {
|
||||
/* We represent CX4 as fibre in the absence of anything
|
||||
better. */
|
||||
case MDIO_PMAPMD_CTRL2_10G_CX4:
|
||||
ecmd->speed = SPEED_10000;
|
||||
ecmd->port = PORT_FIBRE;
|
||||
ecmd->supported = SUPPORTED_FIBRE;
|
||||
ecmd->advertising = ADVERTISED_FIBRE;
|
||||
break;
|
||||
/* 10G Base-T */
|
||||
case MDIO_PMAPMD_CTRL2_10G_BT:
|
||||
ecmd->speed = SPEED_10000;
|
||||
ecmd->port = PORT_TP;
|
||||
ecmd->supported = SUPPORTED_TP | SUPPORTED_10000baseT_Full;
|
||||
ecmd->advertising = (ADVERTISED_FIBRE
|
||||
| ADVERTISED_10000baseT_Full);
|
||||
break;
|
||||
case MDIO_PMAPMD_CTRL2_1G_BT:
|
||||
ecmd->speed = SPEED_1000;
|
||||
ecmd->port = PORT_TP;
|
||||
ecmd->supported = SUPPORTED_TP | SUPPORTED_1000baseT_Full;
|
||||
ecmd->advertising = (ADVERTISED_FIBRE
|
||||
| ADVERTISED_1000baseT_Full);
|
||||
break;
|
||||
case MDIO_PMAPMD_CTRL2_100_BT:
|
||||
ecmd->speed = SPEED_100;
|
||||
ecmd->port = PORT_TP;
|
||||
ecmd->supported = SUPPORTED_TP | SUPPORTED_100baseT_Full;
|
||||
ecmd->advertising = (ADVERTISED_FIBRE
|
||||
| ADVERTISED_100baseT_Full);
|
||||
break;
|
||||
case MDIO_PMAPMD_CTRL2_10_BT:
|
||||
ecmd->speed = SPEED_10;
|
||||
ecmd->port = PORT_TP;
|
||||
ecmd->supported = SUPPORTED_TP | SUPPORTED_10baseT_Full;
|
||||
ecmd->advertising = ADVERTISED_FIBRE | ADVERTISED_10baseT_Full;
|
||||
break;
|
||||
/* All the other defined modes are flavours of
|
||||
* 10G optical */
|
||||
default:
|
||||
ecmd->speed = SPEED_10000;
|
||||
ecmd->port = PORT_FIBRE;
|
||||
ecmd->supported = SUPPORTED_FIBRE;
|
||||
ecmd->advertising = ADVERTISED_FIBRE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* mdio_clause45_set_settings - Set (some of) the PHY settings over MDIO.
|
||||
* @efx: Efx NIC
|
||||
* @ecmd: New settings
|
||||
*
|
||||
* Currently this just enforces that we are _not_ changing the
|
||||
* 'port', 'speed', 'supported' or 'advertising' settings as these
|
||||
* cannot be changed on any currently supported PHY.
|
||||
*/
|
||||
int mdio_clause45_set_settings(struct efx_nic *efx,
|
||||
struct ethtool_cmd *ecmd)
|
||||
{
|
||||
struct ethtool_cmd tmpcmd;
|
||||
mdio_clause45_get_settings(efx, &tmpcmd);
|
||||
/* None of the current PHYs support more than one mode
|
||||
* of operation (and only 10GBT ever will), so keep things
|
||||
* simple for now */
|
||||
if ((ecmd->speed == tmpcmd.speed) && (ecmd->port == tmpcmd.port) &&
|
||||
(ecmd->supported == tmpcmd.supported) &&
|
||||
(ecmd->advertising == tmpcmd.advertising))
|
||||
return 0;
|
||||
return -EOPNOTSUPP;
|
||||
}
|
232
drivers/net/sfc/mdio_10g.h
Normal file
232
drivers/net/sfc/mdio_10g.h
Normal file
@ -0,0 +1,232 @@
|
||||
/****************************************************************************
|
||||
* Driver for Solarflare Solarstorm network controllers and boards
|
||||
* Copyright 2006-2008 Solarflare Communications Inc.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 as published
|
||||
* by the Free Software Foundation, incorporated herein by reference.
|
||||
*/
|
||||
|
||||
#ifndef EFX_MDIO_10G_H
|
||||
#define EFX_MDIO_10G_H
|
||||
|
||||
/*
|
||||
* Definitions needed for doing 10G MDIO as specified in clause 45
|
||||
* MDIO, which do not appear in Linux yet. Also some helper functions.
|
||||
*/
|
||||
|
||||
#include "efx.h"
|
||||
#include "boards.h"
|
||||
|
||||
/* Numbering of the MDIO Manageable Devices (MMDs) */
|
||||
/* Physical Medium Attachment/ Physical Medium Dependent sublayer */
|
||||
#define MDIO_MMD_PMAPMD (1)
|
||||
/* WAN Interface Sublayer */
|
||||
#define MDIO_MMD_WIS (2)
|
||||
/* Physical Coding Sublayer */
|
||||
#define MDIO_MMD_PCS (3)
|
||||
/* PHY Extender Sublayer */
|
||||
#define MDIO_MMD_PHYXS (4)
|
||||
/* Extender Sublayer */
|
||||
#define MDIO_MMD_DTEXS (5)
|
||||
/* Transmission convergence */
|
||||
#define MDIO_MMD_TC (6)
|
||||
/* Auto negotiation */
|
||||
#define MDIO_MMD_AN (7)
|
||||
|
||||
/* Generic register locations */
|
||||
#define MDIO_MMDREG_CTRL1 (0)
|
||||
#define MDIO_MMDREG_STAT1 (1)
|
||||
#define MDIO_MMDREG_IDHI (2)
|
||||
#define MDIO_MMDREG_IDLOW (3)
|
||||
#define MDIO_MMDREG_SPEED (4)
|
||||
#define MDIO_MMDREG_DEVS0 (5)
|
||||
#define MDIO_MMDREG_DEVS1 (6)
|
||||
#define MDIO_MMDREG_CTRL2 (7)
|
||||
#define MDIO_MMDREG_STAT2 (8)
|
||||
|
||||
/* Bits in MMDREG_CTRL1 */
|
||||
/* Reset */
|
||||
#define MDIO_MMDREG_CTRL1_RESET_LBN (15)
|
||||
#define MDIO_MMDREG_CTRL1_RESET_WIDTH (1)
|
||||
|
||||
/* Bits in MMDREG_STAT1 */
|
||||
#define MDIO_MMDREG_STAT1_FAULT_LBN (7)
|
||||
#define MDIO_MMDREG_STAT1_FAULT_WIDTH (1)
|
||||
/* Link state */
|
||||
#define MDIO_MMDREG_STAT1_LINK_LBN (2)
|
||||
#define MDIO_MMDREG_STAT1_LINK_WIDTH (1)
|
||||
|
||||
/* Bits in ID reg */
|
||||
#define MDIO_ID_REV(_id32) (_id32 & 0xf)
|
||||
#define MDIO_ID_MODEL(_id32) ((_id32 >> 4) & 0x3f)
|
||||
#define MDIO_ID_OUI(_id32) (_id32 >> 10)
|
||||
|
||||
/* Bits in MMDREG_DEVS0. Someone thoughtfully layed things out
|
||||
* so the 'bit present' bit number of an MMD is the number of
|
||||
* that MMD */
|
||||
#define DEV_PRESENT_BIT(_b) (1 << _b)
|
||||
|
||||
#define MDIO_MMDREG_DEVS0_PHYXS DEV_PRESENT_BIT(MDIO_MMD_PHYXS)
|
||||
#define MDIO_MMDREG_DEVS0_PCS DEV_PRESENT_BIT(MDIO_MMD_PCS)
|
||||
#define MDIO_MMDREG_DEVS0_PMAPMD DEV_PRESENT_BIT(MDIO_MMD_PMAPMD)
|
||||
|
||||
/* Bits in MMDREG_STAT2 */
|
||||
#define MDIO_MMDREG_STAT2_PRESENT_VAL (2)
|
||||
#define MDIO_MMDREG_STAT2_PRESENT_LBN (14)
|
||||
#define MDIO_MMDREG_STAT2_PRESENT_WIDTH (2)
|
||||
|
||||
/* PMA type (4 bits) */
|
||||
#define MDIO_PMAPMD_CTRL2_10G_CX4 (0x0)
|
||||
#define MDIO_PMAPMD_CTRL2_10G_EW (0x1)
|
||||
#define MDIO_PMAPMD_CTRL2_10G_LW (0x2)
|
||||
#define MDIO_PMAPMD_CTRL2_10G_SW (0x3)
|
||||
#define MDIO_PMAPMD_CTRL2_10G_LX4 (0x4)
|
||||
#define MDIO_PMAPMD_CTRL2_10G_ER (0x5)
|
||||
#define MDIO_PMAPMD_CTRL2_10G_LR (0x6)
|
||||
#define MDIO_PMAPMD_CTRL2_10G_SR (0x7)
|
||||
/* Reserved */
|
||||
#define MDIO_PMAPMD_CTRL2_10G_BT (0x9)
|
||||
/* Reserved */
|
||||
/* Reserved */
|
||||
#define MDIO_PMAPMD_CTRL2_1G_BT (0xc)
|
||||
/* Reserved */
|
||||
#define MDIO_PMAPMD_CTRL2_100_BT (0xe)
|
||||
#define MDIO_PMAPMD_CTRL2_10_BT (0xf)
|
||||
#define MDIO_PMAPMD_CTRL2_TYPE_MASK (0xf)
|
||||
|
||||
/* /\* PHY XGXS lane state *\/ */
|
||||
#define MDIO_PHYXS_LANE_STATE (0x18)
|
||||
#define MDIO_PHYXS_LANE_ALIGNED_LBN (12)
|
||||
|
||||
/* AN registers */
|
||||
#define MDIO_AN_STATUS (1)
|
||||
#define MDIO_AN_STATUS_XNP_LBN (7)
|
||||
#define MDIO_AN_STATUS_PAGE_LBN (6)
|
||||
#define MDIO_AN_STATUS_AN_DONE_LBN (5)
|
||||
#define MDIO_AN_STATUS_LP_AN_CAP_LBN (0)
|
||||
|
||||
#define MDIO_AN_10GBT_STATUS (33)
|
||||
#define MDIO_AN_10GBT_STATUS_MS_FLT_LBN (15) /* MASTER/SLAVE config fault */
|
||||
#define MDIO_AN_10GBT_STATUS_MS_LBN (14) /* MASTER/SLAVE config */
|
||||
#define MDIO_AN_10GBT_STATUS_LOC_OK_LBN (13) /* Local OK */
|
||||
#define MDIO_AN_10GBT_STATUS_REM_OK_LBN (12) /* Remote OK */
|
||||
#define MDIO_AN_10GBT_STATUS_LP_10G_LBN (11) /* Link partner is 10GBT capable */
|
||||
#define MDIO_AN_10GBT_STATUS_LP_LTA_LBN (10) /* LP loop timing ability */
|
||||
#define MDIO_AN_10GBT_STATUS_LP_TRR_LBN (9) /* LP Training Reset Request */
|
||||
|
||||
|
||||
/* Packing of the prt and dev arguments of clause 45 style MDIO into a
|
||||
* single int so they can be passed into the mdio_read/write functions
|
||||
* that currently exist. Note that as Falcon is the only current user,
|
||||
* the packed form is chosen to match what Falcon needs to write into
|
||||
* a register. This is checked at compile-time so do not change it. If
|
||||
* your target chip needs things layed out differently you will need
|
||||
* to unpack the arguments in your chip-specific mdio functions.
|
||||
*/
|
||||
/* These are defined by the standard. */
|
||||
#define MDIO45_PRT_ID_WIDTH (5)
|
||||
#define MDIO45_DEV_ID_WIDTH (5)
|
||||
|
||||
/* The prt ID is just packed in immediately to the left of the dev ID */
|
||||
#define MDIO45_PRT_DEV_WIDTH (MDIO45_PRT_ID_WIDTH + MDIO45_DEV_ID_WIDTH)
|
||||
|
||||
#define MDIO45_PRT_ID_MASK ((1 << MDIO45_PRT_DEV_WIDTH) - 1)
|
||||
/* This is the prt + dev extended by 1 bit to hold the 'is clause 45' flag. */
|
||||
#define MDIO45_XPRT_ID_WIDTH (MDIO45_PRT_DEV_WIDTH + 1)
|
||||
#define MDIO45_XPRT_ID_MASK ((1 << MDIO45_XPRT_ID_WIDTH) - 1)
|
||||
#define MDIO45_XPRT_ID_IS10G (1 << (MDIO45_XPRT_ID_WIDTH - 1))
|
||||
|
||||
|
||||
#define MDIO45_PRT_ID_COMP_LBN MDIO45_DEV_ID_WIDTH
|
||||
#define MDIO45_PRT_ID_COMP_WIDTH MDIO45_PRT_ID_WIDTH
|
||||
#define MDIO45_DEV_ID_COMP_LBN 0
|
||||
#define MDIO45_DEV_ID_COMP_WIDTH MDIO45_DEV_ID_WIDTH
|
||||
|
||||
/* Compose port and device into a phy_id */
|
||||
static inline int mdio_clause45_pack(u8 prt, u8 dev)
|
||||
{
|
||||
efx_dword_t phy_id;
|
||||
EFX_POPULATE_DWORD_2(phy_id, MDIO45_PRT_ID_COMP, prt,
|
||||
MDIO45_DEV_ID_COMP, dev);
|
||||
return MDIO45_XPRT_ID_IS10G | EFX_DWORD_VAL(phy_id);
|
||||
}
|
||||
|
||||
static inline void mdio_clause45_unpack(u32 val, u8 *prt, u8 *dev)
|
||||
{
|
||||
efx_dword_t phy_id;
|
||||
EFX_POPULATE_DWORD_1(phy_id, EFX_DWORD_0, val);
|
||||
*prt = EFX_DWORD_FIELD(phy_id, MDIO45_PRT_ID_COMP);
|
||||
*dev = EFX_DWORD_FIELD(phy_id, MDIO45_DEV_ID_COMP);
|
||||
}
|
||||
|
||||
static inline int mdio_clause45_read(struct efx_nic *efx,
|
||||
u8 prt, u8 dev, u16 addr)
|
||||
{
|
||||
return efx->mii.mdio_read(efx->net_dev,
|
||||
mdio_clause45_pack(prt, dev), addr);
|
||||
}
|
||||
|
||||
static inline void mdio_clause45_write(struct efx_nic *efx,
|
||||
u8 prt, u8 dev, u16 addr, int value)
|
||||
{
|
||||
efx->mii.mdio_write(efx->net_dev,
|
||||
mdio_clause45_pack(prt, dev), addr, value);
|
||||
}
|
||||
|
||||
|
||||
static inline u32 mdio_clause45_read_id(struct efx_nic *efx, int mmd)
|
||||
{
|
||||
int phy_id = efx->mii.phy_id;
|
||||
u16 id_low = mdio_clause45_read(efx, phy_id, mmd, MDIO_MMDREG_IDLOW);
|
||||
u16 id_hi = mdio_clause45_read(efx, phy_id, mmd, MDIO_MMDREG_IDHI);
|
||||
return (id_hi << 16) | (id_low);
|
||||
}
|
||||
|
||||
static inline int mdio_clause45_phyxgxs_lane_sync(struct efx_nic *efx)
|
||||
{
|
||||
int i, sync, lane_status;
|
||||
|
||||
for (i = 0; i < 2; ++i)
|
||||
lane_status = mdio_clause45_read(efx, efx->mii.phy_id,
|
||||
MDIO_MMD_PHYXS,
|
||||
MDIO_PHYXS_LANE_STATE);
|
||||
|
||||
sync = (lane_status & (1 << MDIO_PHYXS_LANE_ALIGNED_LBN)) != 0;
|
||||
if (!sync)
|
||||
EFX_INFO(efx, "XGXS lane status: %x\n", lane_status);
|
||||
return sync;
|
||||
}
|
||||
|
||||
extern const char *mdio_clause45_mmd_name(int mmd);
|
||||
|
||||
/*
|
||||
* Reset a specific MMD and wait for reset to clear.
|
||||
* Return number of spins left (>0) on success, -%ETIMEDOUT on failure.
|
||||
*
|
||||
* This function will sleep
|
||||
*/
|
||||
extern int mdio_clause45_reset_mmd(struct efx_nic *efx, int mmd,
|
||||
int spins, int spintime);
|
||||
|
||||
/* As mdio_clause45_check_mmd but for multiple MMDs */
|
||||
int mdio_clause45_check_mmds(struct efx_nic *efx,
|
||||
unsigned int mmd_mask, unsigned int fatal_mask);
|
||||
|
||||
/* Check the link status of specified mmds in bit mask */
|
||||
extern int mdio_clause45_links_ok(struct efx_nic *efx,
|
||||
unsigned int mmd_mask);
|
||||
|
||||
/* Read (some of) the PHY settings over MDIO */
|
||||
extern void mdio_clause45_get_settings(struct efx_nic *efx,
|
||||
struct ethtool_cmd *ecmd);
|
||||
|
||||
/* Set (some of) the PHY settings over MDIO */
|
||||
extern int mdio_clause45_set_settings(struct efx_nic *efx,
|
||||
struct ethtool_cmd *ecmd);
|
||||
|
||||
/* Wait for specified MMDs to exit reset within a timeout */
|
||||
extern int mdio_clause45_wait_reset_mmds(struct efx_nic *efx,
|
||||
unsigned int mmd_mask);
|
||||
|
||||
#endif /* EFX_MDIO_10G_H */
|
883
drivers/net/sfc/net_driver.h
Normal file
883
drivers/net/sfc/net_driver.h
Normal file
@ -0,0 +1,883 @@
|
||||
/****************************************************************************
|
||||
* Driver for Solarflare Solarstorm network controllers and boards
|
||||
* Copyright 2005-2006 Fen Systems Ltd.
|
||||
* Copyright 2005-2008 Solarflare Communications Inc.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 as published
|
||||
* by the Free Software Foundation, incorporated herein by reference.
|
||||
*/
|
||||
|
||||
/* Common definitions for all Efx net driver code */
|
||||
|
||||
#ifndef EFX_NET_DRIVER_H
|
||||
#define EFX_NET_DRIVER_H
|
||||
|
||||
#include <linux/version.h>
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/etherdevice.h>
|
||||
#include <linux/ethtool.h>
|
||||
#include <linux/if_vlan.h>
|
||||
#include <linux/timer.h>
|
||||
#include <linux/mii.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/highmem.h>
|
||||
#include <linux/workqueue.h>
|
||||
#include <linux/inet_lro.h>
|
||||
|
||||
#include "enum.h"
|
||||
#include "bitfield.h"
|
||||
#include "i2c-direct.h"
|
||||
|
||||
#define EFX_MAX_LRO_DESCRIPTORS 8
|
||||
#define EFX_MAX_LRO_AGGR MAX_SKB_FRAGS
|
||||
|
||||
/**************************************************************************
|
||||
*
|
||||
* Build definitions
|
||||
*
|
||||
**************************************************************************/
|
||||
#ifndef EFX_DRIVER_NAME
|
||||
#define EFX_DRIVER_NAME "sfc"
|
||||
#endif
|
||||
#define EFX_DRIVER_VERSION "2.2.0136"
|
||||
|
||||
#ifdef EFX_ENABLE_DEBUG
|
||||
#define EFX_BUG_ON_PARANOID(x) BUG_ON(x)
|
||||
#define EFX_WARN_ON_PARANOID(x) WARN_ON(x)
|
||||
#else
|
||||
#define EFX_BUG_ON_PARANOID(x) do {} while (0)
|
||||
#define EFX_WARN_ON_PARANOID(x) do {} while (0)
|
||||
#endif
|
||||
|
||||
#define NET_DEV_REGISTERED(efx) \
|
||||
((efx)->net_dev->reg_state == NETREG_REGISTERED)
|
||||
|
||||
/* Include net device name in log messages if it has been registered.
|
||||
* Use efx->name not efx->net_dev->name so that races with (un)registration
|
||||
* are harmless.
|
||||
*/
|
||||
#define NET_DEV_NAME(efx) (NET_DEV_REGISTERED(efx) ? (efx)->name : "")
|
||||
|
||||
/* Un-rate-limited logging */
|
||||
#define EFX_ERR(efx, fmt, args...) \
|
||||
dev_err(&((efx)->pci_dev->dev), "ERR: %s " fmt, NET_DEV_NAME(efx), ##args)
|
||||
|
||||
#define EFX_INFO(efx, fmt, args...) \
|
||||
dev_info(&((efx)->pci_dev->dev), "INFO: %s " fmt, NET_DEV_NAME(efx), ##args)
|
||||
|
||||
#ifdef EFX_ENABLE_DEBUG
|
||||
#define EFX_LOG(efx, fmt, args...) \
|
||||
dev_info(&((efx)->pci_dev->dev), "DBG: %s " fmt, NET_DEV_NAME(efx), ##args)
|
||||
#else
|
||||
#define EFX_LOG(efx, fmt, args...) \
|
||||
dev_dbg(&((efx)->pci_dev->dev), "DBG: %s " fmt, NET_DEV_NAME(efx), ##args)
|
||||
#endif
|
||||
|
||||
#define EFX_TRACE(efx, fmt, args...) do {} while (0)
|
||||
|
||||
#define EFX_REGDUMP(efx, fmt, args...) do {} while (0)
|
||||
|
||||
/* Rate-limited logging */
|
||||
#define EFX_ERR_RL(efx, fmt, args...) \
|
||||
do {if (net_ratelimit()) EFX_ERR(efx, fmt, ##args); } while (0)
|
||||
|
||||
#define EFX_INFO_RL(efx, fmt, args...) \
|
||||
do {if (net_ratelimit()) EFX_INFO(efx, fmt, ##args); } while (0)
|
||||
|
||||
#define EFX_LOG_RL(efx, fmt, args...) \
|
||||
do {if (net_ratelimit()) EFX_LOG(efx, fmt, ##args); } while (0)
|
||||
|
||||
/* Kernel headers may redefine inline anyway */
|
||||
#ifndef inline
|
||||
#define inline inline __attribute__ ((always_inline))
|
||||
#endif
|
||||
|
||||
/**************************************************************************
|
||||
*
|
||||
* Efx data structures
|
||||
*
|
||||
**************************************************************************/
|
||||
|
||||
#define EFX_MAX_CHANNELS 32
|
||||
#define EFX_MAX_TX_QUEUES 1
|
||||
#define EFX_MAX_RX_QUEUES EFX_MAX_CHANNELS
|
||||
|
||||
/**
|
||||
* struct efx_special_buffer - An Efx special buffer
|
||||
* @addr: CPU base address of the buffer
|
||||
* @dma_addr: DMA base address of the buffer
|
||||
* @len: Buffer length, in bytes
|
||||
* @index: Buffer index within controller;s buffer table
|
||||
* @entries: Number of buffer table entries
|
||||
*
|
||||
* Special buffers are used for the event queues and the TX and RX
|
||||
* descriptor queues for each channel. They are *not* used for the
|
||||
* actual transmit and receive buffers.
|
||||
*
|
||||
* Note that for Falcon, TX and RX descriptor queues live in host memory.
|
||||
* Allocation and freeing procedures must take this into account.
|
||||
*/
|
||||
struct efx_special_buffer {
|
||||
void *addr;
|
||||
dma_addr_t dma_addr;
|
||||
unsigned int len;
|
||||
int index;
|
||||
int entries;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct efx_tx_buffer - An Efx TX buffer
|
||||
* @skb: The associated socket buffer.
|
||||
* Set only on the final fragment of a packet; %NULL for all other
|
||||
* fragments. When this fragment completes, then we can free this
|
||||
* skb.
|
||||
* @dma_addr: DMA address of the fragment.
|
||||
* @len: Length of this fragment.
|
||||
* This field is zero when the queue slot is empty.
|
||||
* @continuation: True if this fragment is not the end of a packet.
|
||||
* @unmap_single: True if pci_unmap_single should be used.
|
||||
* @unmap_addr: DMA address to unmap
|
||||
* @unmap_len: Length of this fragment to unmap
|
||||
*/
|
||||
struct efx_tx_buffer {
|
||||
const struct sk_buff *skb;
|
||||
dma_addr_t dma_addr;
|
||||
unsigned short len;
|
||||
unsigned char continuation;
|
||||
unsigned char unmap_single;
|
||||
dma_addr_t unmap_addr;
|
||||
unsigned short unmap_len;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct efx_tx_queue - An Efx TX queue
|
||||
*
|
||||
* This is a ring buffer of TX fragments.
|
||||
* Since the TX completion path always executes on the same
|
||||
* CPU and the xmit path can operate on different CPUs,
|
||||
* performance is increased by ensuring that the completion
|
||||
* path and the xmit path operate on different cache lines.
|
||||
* This is particularly important if the xmit path is always
|
||||
* executing on one CPU which is different from the completion
|
||||
* path. There is also a cache line for members which are
|
||||
* read but not written on the fast path.
|
||||
*
|
||||
* @efx: The associated Efx NIC
|
||||
* @queue: DMA queue number
|
||||
* @used: Queue is used by net driver
|
||||
* @channel: The associated channel
|
||||
* @buffer: The software buffer ring
|
||||
* @txd: The hardware descriptor ring
|
||||
* @read_count: Current read pointer.
|
||||
* This is the number of buffers that have been removed from both rings.
|
||||
* @stopped: Stopped flag.
|
||||
* Set if this TX queue is currently stopping its port.
|
||||
* @insert_count: Current insert pointer
|
||||
* This is the number of buffers that have been added to the
|
||||
* software ring.
|
||||
* @write_count: Current write pointer
|
||||
* This is the number of buffers that have been added to the
|
||||
* hardware ring.
|
||||
* @old_read_count: The value of read_count when last checked.
|
||||
* This is here for performance reasons. The xmit path will
|
||||
* only get the up-to-date value of read_count if this
|
||||
* variable indicates that the queue is full. This is to
|
||||
* avoid cache-line ping-pong between the xmit path and the
|
||||
* completion path.
|
||||
*/
|
||||
struct efx_tx_queue {
|
||||
/* Members which don't change on the fast path */
|
||||
struct efx_nic *efx ____cacheline_aligned_in_smp;
|
||||
int queue;
|
||||
int used;
|
||||
struct efx_channel *channel;
|
||||
struct efx_nic *nic;
|
||||
struct efx_tx_buffer *buffer;
|
||||
struct efx_special_buffer txd;
|
||||
|
||||
/* Members used mainly on the completion path */
|
||||
unsigned int read_count ____cacheline_aligned_in_smp;
|
||||
int stopped;
|
||||
|
||||
/* Members used only on the xmit path */
|
||||
unsigned int insert_count ____cacheline_aligned_in_smp;
|
||||
unsigned int write_count;
|
||||
unsigned int old_read_count;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct efx_rx_buffer - An Efx RX data buffer
|
||||
* @dma_addr: DMA base address of the buffer
|
||||
* @skb: The associated socket buffer, if any.
|
||||
* If both this and page are %NULL, the buffer slot is currently free.
|
||||
* @page: The associated page buffer, if any.
|
||||
* If both this and skb are %NULL, the buffer slot is currently free.
|
||||
* @data: Pointer to ethernet header
|
||||
* @len: Buffer length, in bytes.
|
||||
* @unmap_addr: DMA address to unmap
|
||||
*/
|
||||
struct efx_rx_buffer {
|
||||
dma_addr_t dma_addr;
|
||||
struct sk_buff *skb;
|
||||
struct page *page;
|
||||
char *data;
|
||||
unsigned int len;
|
||||
dma_addr_t unmap_addr;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct efx_rx_queue - An Efx RX queue
|
||||
* @efx: The associated Efx NIC
|
||||
* @queue: DMA queue number
|
||||
* @used: Queue is used by net driver
|
||||
* @channel: The associated channel
|
||||
* @buffer: The software buffer ring
|
||||
* @rxd: The hardware descriptor ring
|
||||
* @added_count: Number of buffers added to the receive queue.
|
||||
* @notified_count: Number of buffers given to NIC (<= @added_count).
|
||||
* @removed_count: Number of buffers removed from the receive queue.
|
||||
* @add_lock: Receive queue descriptor add spin lock.
|
||||
* This lock must be held in order to add buffers to the RX
|
||||
* descriptor ring (rxd and buffer) and to update added_count (but
|
||||
* not removed_count).
|
||||
* @max_fill: RX descriptor maximum fill level (<= ring size)
|
||||
* @fast_fill_trigger: RX descriptor fill level that will trigger a fast fill
|
||||
* (<= @max_fill)
|
||||
* @fast_fill_limit: The level to which a fast fill will fill
|
||||
* (@fast_fill_trigger <= @fast_fill_limit <= @max_fill)
|
||||
* @min_fill: RX descriptor minimum non-zero fill level.
|
||||
* This records the minimum fill level observed when a ring
|
||||
* refill was triggered.
|
||||
* @min_overfill: RX descriptor minimum overflow fill level.
|
||||
* This records the minimum fill level at which RX queue
|
||||
* overflow was observed. It should never be set.
|
||||
* @alloc_page_count: RX allocation strategy counter.
|
||||
* @alloc_skb_count: RX allocation strategy counter.
|
||||
* @work: Descriptor push work thread
|
||||
* @buf_page: Page for next RX buffer.
|
||||
* We can use a single page for multiple RX buffers. This tracks
|
||||
* the remaining space in the allocation.
|
||||
* @buf_dma_addr: Page's DMA address.
|
||||
* @buf_data: Page's host address.
|
||||
*/
|
||||
struct efx_rx_queue {
|
||||
struct efx_nic *efx;
|
||||
int queue;
|
||||
int used;
|
||||
struct efx_channel *channel;
|
||||
struct efx_rx_buffer *buffer;
|
||||
struct efx_special_buffer rxd;
|
||||
|
||||
int added_count;
|
||||
int notified_count;
|
||||
int removed_count;
|
||||
spinlock_t add_lock;
|
||||
unsigned int max_fill;
|
||||
unsigned int fast_fill_trigger;
|
||||
unsigned int fast_fill_limit;
|
||||
unsigned int min_fill;
|
||||
unsigned int min_overfill;
|
||||
unsigned int alloc_page_count;
|
||||
unsigned int alloc_skb_count;
|
||||
struct delayed_work work;
|
||||
unsigned int slow_fill_count;
|
||||
|
||||
struct page *buf_page;
|
||||
dma_addr_t buf_dma_addr;
|
||||
char *buf_data;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct efx_buffer - An Efx general-purpose buffer
|
||||
* @addr: host base address of the buffer
|
||||
* @dma_addr: DMA base address of the buffer
|
||||
* @len: Buffer length, in bytes
|
||||
*
|
||||
* Falcon uses these buffers for its interrupt status registers and
|
||||
* MAC stats dumps.
|
||||
*/
|
||||
struct efx_buffer {
|
||||
void *addr;
|
||||
dma_addr_t dma_addr;
|
||||
unsigned int len;
|
||||
};
|
||||
|
||||
|
||||
/* Flags for channel->used_flags */
|
||||
#define EFX_USED_BY_RX 1
|
||||
#define EFX_USED_BY_TX 2
|
||||
#define EFX_USED_BY_RX_TX (EFX_USED_BY_RX | EFX_USED_BY_TX)
|
||||
|
||||
enum efx_rx_alloc_method {
|
||||
RX_ALLOC_METHOD_AUTO = 0,
|
||||
RX_ALLOC_METHOD_SKB = 1,
|
||||
RX_ALLOC_METHOD_PAGE = 2,
|
||||
};
|
||||
|
||||
/**
|
||||
* struct efx_channel - An Efx channel
|
||||
*
|
||||
* A channel comprises an event queue, at least one TX queue, at least
|
||||
* one RX queue, and an associated tasklet for processing the event
|
||||
* queue.
|
||||
*
|
||||
* @efx: Associated Efx NIC
|
||||
* @evqnum: Event queue number
|
||||
* @channel: Channel instance number
|
||||
* @used_flags: Channel is used by net driver
|
||||
* @enabled: Channel enabled indicator
|
||||
* @irq: IRQ number (MSI and MSI-X only)
|
||||
* @has_interrupt: Channel has an interrupt
|
||||
* @irq_moderation: IRQ moderation value (in us)
|
||||
* @napi_dev: Net device used with NAPI
|
||||
* @napi_str: NAPI control structure
|
||||
* @reset_work: Scheduled reset work thread
|
||||
* @work_pending: Is work pending via NAPI?
|
||||
* @eventq: Event queue buffer
|
||||
* @eventq_read_ptr: Event queue read pointer
|
||||
* @last_eventq_read_ptr: Last event queue read pointer value.
|
||||
* @eventq_magic: Event queue magic value for driver-generated test events
|
||||
* @lro_mgr: LRO state
|
||||
* @rx_alloc_level: Watermark based heuristic counter for pushing descriptors
|
||||
* and diagnostic counters
|
||||
* @rx_alloc_push_pages: RX allocation method currently in use for pushing
|
||||
* descriptors
|
||||
* @rx_alloc_pop_pages: RX allocation method currently in use for popping
|
||||
* descriptors
|
||||
* @n_rx_tobe_disc: Count of RX_TOBE_DISC errors
|
||||
* @n_rx_ip_frag_err: Count of RX IP fragment errors
|
||||
* @n_rx_ip_hdr_chksum_err: Count of RX IP header checksum errors
|
||||
* @n_rx_tcp_udp_chksum_err: Count of RX TCP and UDP checksum errors
|
||||
* @n_rx_frm_trunc: Count of RX_FRM_TRUNC errors
|
||||
* @n_rx_overlength: Count of RX_OVERLENGTH errors
|
||||
* @n_skbuff_leaks: Count of skbuffs leaked due to RX overrun
|
||||
*/
|
||||
struct efx_channel {
|
||||
struct efx_nic *efx;
|
||||
int evqnum;
|
||||
int channel;
|
||||
int used_flags;
|
||||
int enabled;
|
||||
int irq;
|
||||
unsigned int has_interrupt;
|
||||
unsigned int irq_moderation;
|
||||
struct net_device *napi_dev;
|
||||
struct napi_struct napi_str;
|
||||
struct work_struct reset_work;
|
||||
int work_pending;
|
||||
struct efx_special_buffer eventq;
|
||||
unsigned int eventq_read_ptr;
|
||||
unsigned int last_eventq_read_ptr;
|
||||
unsigned int eventq_magic;
|
||||
|
||||
struct net_lro_mgr lro_mgr;
|
||||
int rx_alloc_level;
|
||||
int rx_alloc_push_pages;
|
||||
int rx_alloc_pop_pages;
|
||||
|
||||
unsigned n_rx_tobe_disc;
|
||||
unsigned n_rx_ip_frag_err;
|
||||
unsigned n_rx_ip_hdr_chksum_err;
|
||||
unsigned n_rx_tcp_udp_chksum_err;
|
||||
unsigned n_rx_frm_trunc;
|
||||
unsigned n_rx_overlength;
|
||||
unsigned n_skbuff_leaks;
|
||||
|
||||
/* Used to pipeline received packets in order to optimise memory
|
||||
* access with prefetches.
|
||||
*/
|
||||
struct efx_rx_buffer *rx_pkt;
|
||||
int rx_pkt_csummed;
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* struct efx_blinker - S/W LED blinking context
|
||||
* @led_num: LED ID (board-specific meaning)
|
||||
* @state: Current state - on or off
|
||||
* @resubmit: Timer resubmission flag
|
||||
* @timer: Control timer for blinking
|
||||
*/
|
||||
struct efx_blinker {
|
||||
int led_num;
|
||||
int state;
|
||||
int resubmit;
|
||||
struct timer_list timer;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* struct efx_board - board information
|
||||
* @type: Board model type
|
||||
* @major: Major rev. ('A', 'B' ...)
|
||||
* @minor: Minor rev. (0, 1, ...)
|
||||
* @init: Initialisation function
|
||||
* @init_leds: Sets up board LEDs
|
||||
* @set_fault_led: Turns the fault LED on or off
|
||||
* @blink: Starts/stops blinking
|
||||
* @blinker: used to blink LEDs in software
|
||||
*/
|
||||
struct efx_board {
|
||||
int type;
|
||||
int major;
|
||||
int minor;
|
||||
int (*init) (struct efx_nic *nic);
|
||||
/* As the LEDs are typically attached to the PHY, LEDs
|
||||
* have a separate init callback that happens later than
|
||||
* board init. */
|
||||
int (*init_leds)(struct efx_nic *efx);
|
||||
void (*set_fault_led) (struct efx_nic *efx, int state);
|
||||
void (*blink) (struct efx_nic *efx, int start);
|
||||
struct efx_blinker blinker;
|
||||
};
|
||||
|
||||
enum efx_int_mode {
|
||||
/* Be careful if altering to correct macro below */
|
||||
EFX_INT_MODE_MSIX = 0,
|
||||
EFX_INT_MODE_MSI = 1,
|
||||
EFX_INT_MODE_LEGACY = 2,
|
||||
EFX_INT_MODE_MAX /* Insert any new items before this */
|
||||
};
|
||||
#define EFX_INT_MODE_USE_MSI(x) (((x)->interrupt_mode) <= EFX_INT_MODE_MSI)
|
||||
|
||||
enum phy_type {
|
||||
PHY_TYPE_NONE = 0,
|
||||
PHY_TYPE_CX4_RTMR = 1,
|
||||
PHY_TYPE_1G_ALASKA = 2,
|
||||
PHY_TYPE_10XPRESS = 3,
|
||||
PHY_TYPE_XFP = 4,
|
||||
PHY_TYPE_PM8358 = 6,
|
||||
PHY_TYPE_MAX /* Insert any new items before this */
|
||||
};
|
||||
|
||||
#define PHY_ADDR_INVALID 0xff
|
||||
|
||||
enum nic_state {
|
||||
STATE_INIT = 0,
|
||||
STATE_RUNNING = 1,
|
||||
STATE_FINI = 2,
|
||||
STATE_RESETTING = 3, /* rtnl_lock always held */
|
||||
STATE_DISABLED = 4,
|
||||
STATE_MAX,
|
||||
};
|
||||
|
||||
/*
|
||||
* Alignment of page-allocated RX buffers
|
||||
*
|
||||
* Controls the number of bytes inserted at the start of an RX buffer.
|
||||
* This is the equivalent of NET_IP_ALIGN [which controls the alignment
|
||||
* of the skb->head for hardware DMA].
|
||||
*/
|
||||
#if defined(__i386__) || defined(__x86_64__)
|
||||
#define EFX_PAGE_IP_ALIGN 0
|
||||
#else
|
||||
#define EFX_PAGE_IP_ALIGN NET_IP_ALIGN
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Alignment of the skb->head which wraps a page-allocated RX buffer
|
||||
*
|
||||
* The skb allocated to wrap an rx_buffer can have this alignment. Since
|
||||
* the data is memcpy'd from the rx_buf, it does not need to be equal to
|
||||
* EFX_PAGE_IP_ALIGN.
|
||||
*/
|
||||
#define EFX_PAGE_SKB_ALIGN 2
|
||||
|
||||
/* Forward declaration */
|
||||
struct efx_nic;
|
||||
|
||||
/* Pseudo bit-mask flow control field */
|
||||
enum efx_fc_type {
|
||||
EFX_FC_RX = 1,
|
||||
EFX_FC_TX = 2,
|
||||
EFX_FC_AUTO = 4,
|
||||
};
|
||||
|
||||
/**
|
||||
* struct efx_phy_operations - Efx PHY operations table
|
||||
* @init: Initialise PHY
|
||||
* @fini: Shut down PHY
|
||||
* @reconfigure: Reconfigure PHY (e.g. for new link parameters)
|
||||
* @clear_interrupt: Clear down interrupt
|
||||
* @blink: Blink LEDs
|
||||
* @check_hw: Check hardware
|
||||
* @reset_xaui: Reset XAUI side of PHY for (software sequenced reset)
|
||||
* @mmds: MMD presence mask
|
||||
*/
|
||||
struct efx_phy_operations {
|
||||
int (*init) (struct efx_nic *efx);
|
||||
void (*fini) (struct efx_nic *efx);
|
||||
void (*reconfigure) (struct efx_nic *efx);
|
||||
void (*clear_interrupt) (struct efx_nic *efx);
|
||||
int (*check_hw) (struct efx_nic *efx);
|
||||
void (*reset_xaui) (struct efx_nic *efx);
|
||||
int mmds;
|
||||
};
|
||||
|
||||
/*
|
||||
* Efx extended statistics
|
||||
*
|
||||
* Not all statistics are provided by all supported MACs. The purpose
|
||||
* is this structure is to contain the raw statistics provided by each
|
||||
* MAC.
|
||||
*/
|
||||
struct efx_mac_stats {
|
||||
u64 tx_bytes;
|
||||
u64 tx_good_bytes;
|
||||
u64 tx_bad_bytes;
|
||||
unsigned long tx_packets;
|
||||
unsigned long tx_bad;
|
||||
unsigned long tx_pause;
|
||||
unsigned long tx_control;
|
||||
unsigned long tx_unicast;
|
||||
unsigned long tx_multicast;
|
||||
unsigned long tx_broadcast;
|
||||
unsigned long tx_lt64;
|
||||
unsigned long tx_64;
|
||||
unsigned long tx_65_to_127;
|
||||
unsigned long tx_128_to_255;
|
||||
unsigned long tx_256_to_511;
|
||||
unsigned long tx_512_to_1023;
|
||||
unsigned long tx_1024_to_15xx;
|
||||
unsigned long tx_15xx_to_jumbo;
|
||||
unsigned long tx_gtjumbo;
|
||||
unsigned long tx_collision;
|
||||
unsigned long tx_single_collision;
|
||||
unsigned long tx_multiple_collision;
|
||||
unsigned long tx_excessive_collision;
|
||||
unsigned long tx_deferred;
|
||||
unsigned long tx_late_collision;
|
||||
unsigned long tx_excessive_deferred;
|
||||
unsigned long tx_non_tcpudp;
|
||||
unsigned long tx_mac_src_error;
|
||||
unsigned long tx_ip_src_error;
|
||||
u64 rx_bytes;
|
||||
u64 rx_good_bytes;
|
||||
u64 rx_bad_bytes;
|
||||
unsigned long rx_packets;
|
||||
unsigned long rx_good;
|
||||
unsigned long rx_bad;
|
||||
unsigned long rx_pause;
|
||||
unsigned long rx_control;
|
||||
unsigned long rx_unicast;
|
||||
unsigned long rx_multicast;
|
||||
unsigned long rx_broadcast;
|
||||
unsigned long rx_lt64;
|
||||
unsigned long rx_64;
|
||||
unsigned long rx_65_to_127;
|
||||
unsigned long rx_128_to_255;
|
||||
unsigned long rx_256_to_511;
|
||||
unsigned long rx_512_to_1023;
|
||||
unsigned long rx_1024_to_15xx;
|
||||
unsigned long rx_15xx_to_jumbo;
|
||||
unsigned long rx_gtjumbo;
|
||||
unsigned long rx_bad_lt64;
|
||||
unsigned long rx_bad_64_to_15xx;
|
||||
unsigned long rx_bad_15xx_to_jumbo;
|
||||
unsigned long rx_bad_gtjumbo;
|
||||
unsigned long rx_overflow;
|
||||
unsigned long rx_missed;
|
||||
unsigned long rx_false_carrier;
|
||||
unsigned long rx_symbol_error;
|
||||
unsigned long rx_align_error;
|
||||
unsigned long rx_length_error;
|
||||
unsigned long rx_internal_error;
|
||||
unsigned long rx_good_lt64;
|
||||
};
|
||||
|
||||
/* Number of bits used in a multicast filter hash address */
|
||||
#define EFX_MCAST_HASH_BITS 8
|
||||
|
||||
/* Number of (single-bit) entries in a multicast filter hash */
|
||||
#define EFX_MCAST_HASH_ENTRIES (1 << EFX_MCAST_HASH_BITS)
|
||||
|
||||
/* An Efx multicast filter hash */
|
||||
union efx_multicast_hash {
|
||||
u8 byte[EFX_MCAST_HASH_ENTRIES / 8];
|
||||
efx_oword_t oword[EFX_MCAST_HASH_ENTRIES / sizeof(efx_oword_t) / 8];
|
||||
};
|
||||
|
||||
/**
|
||||
* struct efx_nic - an Efx NIC
|
||||
* @name: Device name (net device name or bus id before net device registered)
|
||||
* @pci_dev: The PCI device
|
||||
* @type: Controller type attributes
|
||||
* @legacy_irq: IRQ number
|
||||
* @workqueue: Workqueue for resets, port reconfigures and the HW monitor
|
||||
* @reset_work: Scheduled reset workitem
|
||||
* @monitor_work: Hardware monitor workitem
|
||||
* @membase_phys: Memory BAR value as physical address
|
||||
* @membase: Memory BAR value
|
||||
* @biu_lock: BIU (bus interface unit) lock
|
||||
* @interrupt_mode: Interrupt mode
|
||||
* @i2c: I2C interface
|
||||
* @board_info: Board-level information
|
||||
* @state: Device state flag. Serialised by the rtnl_lock.
|
||||
* @reset_pending: Pending reset method (normally RESET_TYPE_NONE)
|
||||
* @tx_queue: TX DMA queues
|
||||
* @rx_queue: RX DMA queues
|
||||
* @channel: Channels
|
||||
* @rss_queues: Number of RSS queues
|
||||
* @rx_buffer_len: RX buffer length
|
||||
* @rx_buffer_order: Order (log2) of number of pages for each RX buffer
|
||||
* @irq_status: Interrupt status buffer
|
||||
* @last_irq_cpu: Last CPU to handle interrupt.
|
||||
* This register is written with the SMP processor ID whenever an
|
||||
* interrupt is handled. It is used by falcon_test_interrupt()
|
||||
* to verify that an interrupt has occurred.
|
||||
* @n_rx_nodesc_drop_cnt: RX no descriptor drop count
|
||||
* @nic_data: Hardware dependant state
|
||||
* @mac_lock: MAC access lock. Protects @port_enabled, efx_monitor() and
|
||||
* efx_reconfigure_port()
|
||||
* @port_enabled: Port enabled indicator.
|
||||
* Serialises efx_stop_all(), efx_start_all() and efx_monitor() and
|
||||
* efx_reconfigure_work with kernel interfaces. Safe to read under any
|
||||
* one of the rtnl_lock, mac_lock, or netif_tx_lock, but all three must
|
||||
* be held to modify it.
|
||||
* @port_initialized: Port initialized?
|
||||
* @net_dev: Operating system network device. Consider holding the rtnl lock
|
||||
* @rx_checksum_enabled: RX checksumming enabled
|
||||
* @netif_stop_count: Port stop count
|
||||
* @netif_stop_lock: Port stop lock
|
||||
* @mac_stats: MAC statistics. These include all statistics the MACs
|
||||
* can provide. Generic code converts these into a standard
|
||||
* &struct net_device_stats.
|
||||
* @stats_buffer: DMA buffer for statistics
|
||||
* @stats_lock: Statistics update lock
|
||||
* @mac_address: Permanent MAC address
|
||||
* @phy_type: PHY type
|
||||
* @phy_lock: PHY access lock
|
||||
* @phy_op: PHY interface
|
||||
* @phy_data: PHY private data (including PHY-specific stats)
|
||||
* @mii: PHY interface
|
||||
* @phy_powered: PHY power state
|
||||
* @tx_disabled: PHY transmitter turned off
|
||||
* @link_up: Link status
|
||||
* @link_options: Link options (MII/GMII format)
|
||||
* @n_link_state_changes: Number of times the link has changed state
|
||||
* @promiscuous: Promiscuous flag. Protected by netif_tx_lock.
|
||||
* @multicast_hash: Multicast hash table
|
||||
* @flow_control: Flow control flags - separate RX/TX so can't use link_options
|
||||
* @reconfigure_work: work item for dealing with PHY events
|
||||
*
|
||||
* The @priv field of the corresponding &struct net_device points to
|
||||
* this.
|
||||
*/
|
||||
struct efx_nic {
|
||||
char name[IFNAMSIZ];
|
||||
struct pci_dev *pci_dev;
|
||||
const struct efx_nic_type *type;
|
||||
int legacy_irq;
|
||||
struct workqueue_struct *workqueue;
|
||||
struct work_struct reset_work;
|
||||
struct delayed_work monitor_work;
|
||||
unsigned long membase_phys;
|
||||
void __iomem *membase;
|
||||
spinlock_t biu_lock;
|
||||
enum efx_int_mode interrupt_mode;
|
||||
|
||||
struct efx_i2c_interface i2c;
|
||||
struct efx_board board_info;
|
||||
|
||||
enum nic_state state;
|
||||
enum reset_type reset_pending;
|
||||
|
||||
struct efx_tx_queue tx_queue[EFX_MAX_TX_QUEUES];
|
||||
struct efx_rx_queue rx_queue[EFX_MAX_RX_QUEUES];
|
||||
struct efx_channel channel[EFX_MAX_CHANNELS];
|
||||
|
||||
int rss_queues;
|
||||
unsigned int rx_buffer_len;
|
||||
unsigned int rx_buffer_order;
|
||||
|
||||
struct efx_buffer irq_status;
|
||||
volatile signed int last_irq_cpu;
|
||||
|
||||
unsigned n_rx_nodesc_drop_cnt;
|
||||
|
||||
void *nic_data;
|
||||
|
||||
struct mutex mac_lock;
|
||||
int port_enabled;
|
||||
|
||||
int port_initialized;
|
||||
struct net_device *net_dev;
|
||||
int rx_checksum_enabled;
|
||||
|
||||
atomic_t netif_stop_count;
|
||||
spinlock_t netif_stop_lock;
|
||||
|
||||
struct efx_mac_stats mac_stats;
|
||||
struct efx_buffer stats_buffer;
|
||||
spinlock_t stats_lock;
|
||||
|
||||
unsigned char mac_address[ETH_ALEN];
|
||||
|
||||
enum phy_type phy_type;
|
||||
spinlock_t phy_lock;
|
||||
struct efx_phy_operations *phy_op;
|
||||
void *phy_data;
|
||||
struct mii_if_info mii;
|
||||
|
||||
int link_up;
|
||||
unsigned int link_options;
|
||||
unsigned int n_link_state_changes;
|
||||
|
||||
int promiscuous;
|
||||
union efx_multicast_hash multicast_hash;
|
||||
enum efx_fc_type flow_control;
|
||||
struct work_struct reconfigure_work;
|
||||
|
||||
atomic_t rx_reset;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct efx_nic_type - Efx device type definition
|
||||
* @mem_bar: Memory BAR number
|
||||
* @mem_map_size: Memory BAR mapped size
|
||||
* @txd_ptr_tbl_base: TX descriptor ring base address
|
||||
* @rxd_ptr_tbl_base: RX descriptor ring base address
|
||||
* @buf_tbl_base: Buffer table base address
|
||||
* @evq_ptr_tbl_base: Event queue pointer table base address
|
||||
* @evq_rptr_tbl_base: Event queue read-pointer table base address
|
||||
* @txd_ring_mask: TX descriptor ring size - 1 (must be a power of two - 1)
|
||||
* @rxd_ring_mask: RX descriptor ring size - 1 (must be a power of two - 1)
|
||||
* @evq_size: Event queue size (must be a power of two)
|
||||
* @max_dma_mask: Maximum possible DMA mask
|
||||
* @tx_dma_mask: TX DMA mask
|
||||
* @bug5391_mask: Address mask for bug 5391 workaround
|
||||
* @rx_xoff_thresh: RX FIFO XOFF watermark (bytes)
|
||||
* @rx_xon_thresh: RX FIFO XON watermark (bytes)
|
||||
* @rx_buffer_padding: Padding added to each RX buffer
|
||||
* @max_interrupt_mode: Highest capability interrupt mode supported
|
||||
* from &enum efx_init_mode.
|
||||
* @phys_addr_channels: Number of channels with physically addressed
|
||||
* descriptors
|
||||
*/
|
||||
struct efx_nic_type {
|
||||
unsigned int mem_bar;
|
||||
unsigned int mem_map_size;
|
||||
unsigned int txd_ptr_tbl_base;
|
||||
unsigned int rxd_ptr_tbl_base;
|
||||
unsigned int buf_tbl_base;
|
||||
unsigned int evq_ptr_tbl_base;
|
||||
unsigned int evq_rptr_tbl_base;
|
||||
|
||||
unsigned int txd_ring_mask;
|
||||
unsigned int rxd_ring_mask;
|
||||
unsigned int evq_size;
|
||||
dma_addr_t max_dma_mask;
|
||||
unsigned int tx_dma_mask;
|
||||
unsigned bug5391_mask;
|
||||
|
||||
int rx_xoff_thresh;
|
||||
int rx_xon_thresh;
|
||||
unsigned int rx_buffer_padding;
|
||||
unsigned int max_interrupt_mode;
|
||||
unsigned int phys_addr_channels;
|
||||
};
|
||||
|
||||
/**************************************************************************
|
||||
*
|
||||
* Prototypes and inline functions
|
||||
*
|
||||
*************************************************************************/
|
||||
|
||||
/* Iterate over all used channels */
|
||||
#define efx_for_each_channel(_channel, _efx) \
|
||||
for (_channel = &_efx->channel[0]; \
|
||||
_channel < &_efx->channel[EFX_MAX_CHANNELS]; \
|
||||
_channel++) \
|
||||
if (!_channel->used_flags) \
|
||||
continue; \
|
||||
else
|
||||
|
||||
/* Iterate over all used channels with interrupts */
|
||||
#define efx_for_each_channel_with_interrupt(_channel, _efx) \
|
||||
for (_channel = &_efx->channel[0]; \
|
||||
_channel < &_efx->channel[EFX_MAX_CHANNELS]; \
|
||||
_channel++) \
|
||||
if (!(_channel->used_flags && _channel->has_interrupt)) \
|
||||
continue; \
|
||||
else
|
||||
|
||||
/* Iterate over all used TX queues */
|
||||
#define efx_for_each_tx_queue(_tx_queue, _efx) \
|
||||
for (_tx_queue = &_efx->tx_queue[0]; \
|
||||
_tx_queue < &_efx->tx_queue[EFX_MAX_TX_QUEUES]; \
|
||||
_tx_queue++) \
|
||||
if (!_tx_queue->used) \
|
||||
continue; \
|
||||
else
|
||||
|
||||
/* Iterate over all TX queues belonging to a channel */
|
||||
#define efx_for_each_channel_tx_queue(_tx_queue, _channel) \
|
||||
for (_tx_queue = &_channel->efx->tx_queue[0]; \
|
||||
_tx_queue < &_channel->efx->tx_queue[EFX_MAX_TX_QUEUES]; \
|
||||
_tx_queue++) \
|
||||
if ((!_tx_queue->used) || \
|
||||
(_tx_queue->channel != _channel)) \
|
||||
continue; \
|
||||
else
|
||||
|
||||
/* Iterate over all used RX queues */
|
||||
#define efx_for_each_rx_queue(_rx_queue, _efx) \
|
||||
for (_rx_queue = &_efx->rx_queue[0]; \
|
||||
_rx_queue < &_efx->rx_queue[EFX_MAX_RX_QUEUES]; \
|
||||
_rx_queue++) \
|
||||
if (!_rx_queue->used) \
|
||||
continue; \
|
||||
else
|
||||
|
||||
/* Iterate over all RX queues belonging to a channel */
|
||||
#define efx_for_each_channel_rx_queue(_rx_queue, _channel) \
|
||||
for (_rx_queue = &_channel->efx->rx_queue[0]; \
|
||||
_rx_queue < &_channel->efx->rx_queue[EFX_MAX_RX_QUEUES]; \
|
||||
_rx_queue++) \
|
||||
if ((!_rx_queue->used) || \
|
||||
(_rx_queue->channel != _channel)) \
|
||||
continue; \
|
||||
else
|
||||
|
||||
/* Returns a pointer to the specified receive buffer in the RX
|
||||
* descriptor queue.
|
||||
*/
|
||||
static inline struct efx_rx_buffer *efx_rx_buffer(struct efx_rx_queue *rx_queue,
|
||||
unsigned int index)
|
||||
{
|
||||
return (&rx_queue->buffer[index]);
|
||||
}
|
||||
|
||||
/* Set bit in a little-endian bitfield */
|
||||
static inline void set_bit_le(int nr, unsigned char *addr)
|
||||
{
|
||||
addr[nr / 8] |= (1 << (nr % 8));
|
||||
}
|
||||
|
||||
/* Clear bit in a little-endian bitfield */
|
||||
static inline void clear_bit_le(int nr, unsigned char *addr)
|
||||
{
|
||||
addr[nr / 8] &= ~(1 << (nr % 8));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* EFX_MAX_FRAME_LEN - calculate maximum frame length
|
||||
*
|
||||
* This calculates the maximum frame length that will be used for a
|
||||
* given MTU. The frame length will be equal to the MTU plus a
|
||||
* constant amount of header space and padding. This is the quantity
|
||||
* that the net driver will program into the MAC as the maximum frame
|
||||
* length.
|
||||
*
|
||||
* The 10G MAC used in Falcon requires 8-byte alignment on the frame
|
||||
* length, so we round up to the nearest 8.
|
||||
*/
|
||||
#define EFX_MAX_FRAME_LEN(mtu) \
|
||||
((((mtu) + ETH_HLEN + VLAN_HLEN + 4/* FCS */) + 7) & ~7)
|
||||
|
||||
|
||||
#endif /* EFX_NET_DRIVER_H */
|
48
drivers/net/sfc/phy.h
Normal file
48
drivers/net/sfc/phy.h
Normal file
@ -0,0 +1,48 @@
|
||||
/****************************************************************************
|
||||
* Driver for Solarflare Solarstorm network controllers and boards
|
||||
* Copyright 2007 Solarflare Communications Inc.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 as published
|
||||
* by the Free Software Foundation, incorporated herein by reference.
|
||||
*/
|
||||
|
||||
#ifndef EFX_PHY_H
|
||||
#define EFX_PHY_H
|
||||
|
||||
/****************************************************************************
|
||||
* 10Xpress (SFX7101) PHY
|
||||
*/
|
||||
extern struct efx_phy_operations falcon_tenxpress_phy_ops;
|
||||
|
||||
enum tenxpress_state {
|
||||
TENXPRESS_STATUS_OFF = 0,
|
||||
TENXPRESS_STATUS_OTEMP = 1,
|
||||
TENXPRESS_STATUS_NORMAL = 2,
|
||||
};
|
||||
|
||||
extern void tenxpress_set_state(struct efx_nic *efx,
|
||||
enum tenxpress_state state);
|
||||
extern void tenxpress_phy_blink(struct efx_nic *efx, int blink);
|
||||
extern void tenxpress_crc_err(struct efx_nic *efx);
|
||||
|
||||
/****************************************************************************
|
||||
* Exported functions from the driver for XFP optical PHYs
|
||||
*/
|
||||
extern struct efx_phy_operations falcon_xfp_phy_ops;
|
||||
|
||||
/* The QUAKE XFP PHY provides various H/W control states for LEDs */
|
||||
#define QUAKE_LED_LINK_INVAL (0)
|
||||
#define QUAKE_LED_LINK_STAT (1)
|
||||
#define QUAKE_LED_LINK_ACT (2)
|
||||
#define QUAKE_LED_LINK_ACTSTAT (3)
|
||||
#define QUAKE_LED_OFF (4)
|
||||
#define QUAKE_LED_ON (5)
|
||||
#define QUAKE_LED_LINK_INPUT (6) /* Pin is an input. */
|
||||
/* What link the LED tracks */
|
||||
#define QUAKE_LED_TXLINK (0)
|
||||
#define QUAKE_LED_RXLINK (8)
|
||||
|
||||
extern void xfp_set_led(struct efx_nic *p, int led, int state);
|
||||
|
||||
#endif
|
875
drivers/net/sfc/rx.c
Normal file
875
drivers/net/sfc/rx.c
Normal file
@ -0,0 +1,875 @@
|
||||
/****************************************************************************
|
||||
* Driver for Solarflare Solarstorm network controllers and boards
|
||||
* Copyright 2005-2006 Fen Systems Ltd.
|
||||
* Copyright 2005-2008 Solarflare Communications Inc.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 as published
|
||||
* by the Free Software Foundation, incorporated herein by reference.
|
||||
*/
|
||||
|
||||
#include <linux/socket.h>
|
||||
#include <linux/in.h>
|
||||
#include <linux/ip.h>
|
||||
#include <linux/tcp.h>
|
||||
#include <linux/udp.h>
|
||||
#include <net/ip.h>
|
||||
#include <net/checksum.h>
|
||||
#include "net_driver.h"
|
||||
#include "rx.h"
|
||||
#include "efx.h"
|
||||
#include "falcon.h"
|
||||
#include "workarounds.h"
|
||||
|
||||
/* Number of RX descriptors pushed at once. */
|
||||
#define EFX_RX_BATCH 8
|
||||
|
||||
/* Size of buffer allocated for skb header area. */
|
||||
#define EFX_SKB_HEADERS 64u
|
||||
|
||||
/*
|
||||
* rx_alloc_method - RX buffer allocation method
|
||||
*
|
||||
* This driver supports two methods for allocating and using RX buffers:
|
||||
* each RX buffer may be backed by an skb or by an order-n page.
|
||||
*
|
||||
* When LRO is in use then the second method has a lower overhead,
|
||||
* since we don't have to allocate then free skbs on reassembled frames.
|
||||
*
|
||||
* Values:
|
||||
* - RX_ALLOC_METHOD_AUTO = 0
|
||||
* - RX_ALLOC_METHOD_SKB = 1
|
||||
* - RX_ALLOC_METHOD_PAGE = 2
|
||||
*
|
||||
* The heuristic for %RX_ALLOC_METHOD_AUTO is a simple hysteresis count
|
||||
* controlled by the parameters below.
|
||||
*
|
||||
* - Since pushing and popping descriptors are separated by the rx_queue
|
||||
* size, so the watermarks should be ~rxd_size.
|
||||
* - The performance win by using page-based allocation for LRO is less
|
||||
* than the performance hit of using page-based allocation of non-LRO,
|
||||
* so the watermarks should reflect this.
|
||||
*
|
||||
* Per channel we maintain a single variable, updated by each channel:
|
||||
*
|
||||
* rx_alloc_level += (lro_performed ? RX_ALLOC_FACTOR_LRO :
|
||||
* RX_ALLOC_FACTOR_SKB)
|
||||
* Per NAPI poll interval, we constrain rx_alloc_level to 0..MAX (which
|
||||
* limits the hysteresis), and update the allocation strategy:
|
||||
*
|
||||
* rx_alloc_method = (rx_alloc_level > RX_ALLOC_LEVEL_LRO ?
|
||||
* RX_ALLOC_METHOD_PAGE : RX_ALLOC_METHOD_SKB)
|
||||
*/
|
||||
static int rx_alloc_method = RX_ALLOC_METHOD_PAGE;
|
||||
|
||||
#define RX_ALLOC_LEVEL_LRO 0x2000
|
||||
#define RX_ALLOC_LEVEL_MAX 0x3000
|
||||
#define RX_ALLOC_FACTOR_LRO 1
|
||||
#define RX_ALLOC_FACTOR_SKB (-2)
|
||||
|
||||
/* This is the percentage fill level below which new RX descriptors
|
||||
* will be added to the RX descriptor ring.
|
||||
*/
|
||||
static unsigned int rx_refill_threshold = 90;
|
||||
|
||||
/* This is the percentage fill level to which an RX queue will be refilled
|
||||
* when the "RX refill threshold" is reached.
|
||||
*/
|
||||
static unsigned int rx_refill_limit = 95;
|
||||
|
||||
/*
|
||||
* RX maximum head room required.
|
||||
*
|
||||
* This must be at least 1 to prevent overflow and at least 2 to allow
|
||||
* pipelined receives.
|
||||
*/
|
||||
#define EFX_RXD_HEAD_ROOM 2
|
||||
|
||||
/* Macros for zero-order pages (potentially) containing multiple RX buffers */
|
||||
#define RX_DATA_OFFSET(_data) \
|
||||
(((unsigned long) (_data)) & (PAGE_SIZE-1))
|
||||
#define RX_BUF_OFFSET(_rx_buf) \
|
||||
RX_DATA_OFFSET((_rx_buf)->data)
|
||||
|
||||
#define RX_PAGE_SIZE(_efx) \
|
||||
(PAGE_SIZE * (1u << (_efx)->rx_buffer_order))
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
*
|
||||
* Linux generic LRO handling
|
||||
*
|
||||
**************************************************************************
|
||||
*/
|
||||
|
||||
static int efx_lro_get_skb_hdr(struct sk_buff *skb, void **ip_hdr,
|
||||
void **tcpudp_hdr, u64 *hdr_flags, void *priv)
|
||||
{
|
||||
struct efx_channel *channel = (struct efx_channel *)priv;
|
||||
struct iphdr *iph;
|
||||
struct tcphdr *th;
|
||||
|
||||
iph = (struct iphdr *)skb->data;
|
||||
if (skb->protocol != htons(ETH_P_IP) || iph->protocol != IPPROTO_TCP)
|
||||
goto fail;
|
||||
|
||||
th = (struct tcphdr *)(skb->data + iph->ihl * 4);
|
||||
|
||||
*tcpudp_hdr = th;
|
||||
*ip_hdr = iph;
|
||||
*hdr_flags = LRO_IPV4 | LRO_TCP;
|
||||
|
||||
channel->rx_alloc_level += RX_ALLOC_FACTOR_LRO;
|
||||
return 0;
|
||||
fail:
|
||||
channel->rx_alloc_level += RX_ALLOC_FACTOR_SKB;
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int efx_get_frag_hdr(struct skb_frag_struct *frag, void **mac_hdr,
|
||||
void **ip_hdr, void **tcpudp_hdr, u64 *hdr_flags,
|
||||
void *priv)
|
||||
{
|
||||
struct efx_channel *channel = (struct efx_channel *)priv;
|
||||
struct ethhdr *eh;
|
||||
struct iphdr *iph;
|
||||
|
||||
/* We support EtherII and VLAN encapsulated IPv4 */
|
||||
eh = (struct ethhdr *)(page_address(frag->page) + frag->page_offset);
|
||||
*mac_hdr = eh;
|
||||
|
||||
if (eh->h_proto == htons(ETH_P_IP)) {
|
||||
iph = (struct iphdr *)(eh + 1);
|
||||
} else {
|
||||
struct vlan_ethhdr *veh = (struct vlan_ethhdr *)eh;
|
||||
if (veh->h_vlan_encapsulated_proto != htons(ETH_P_IP))
|
||||
goto fail;
|
||||
|
||||
iph = (struct iphdr *)(veh + 1);
|
||||
}
|
||||
*ip_hdr = iph;
|
||||
|
||||
/* We can only do LRO over TCP */
|
||||
if (iph->protocol != IPPROTO_TCP)
|
||||
goto fail;
|
||||
|
||||
*hdr_flags = LRO_IPV4 | LRO_TCP;
|
||||
*tcpudp_hdr = (struct tcphdr *)((u8 *) iph + iph->ihl * 4);
|
||||
|
||||
channel->rx_alloc_level += RX_ALLOC_FACTOR_LRO;
|
||||
return 0;
|
||||
fail:
|
||||
channel->rx_alloc_level += RX_ALLOC_FACTOR_SKB;
|
||||
return -1;
|
||||
}
|
||||
|
||||
int efx_lro_init(struct net_lro_mgr *lro_mgr, struct efx_nic *efx)
|
||||
{
|
||||
size_t s = sizeof(struct net_lro_desc) * EFX_MAX_LRO_DESCRIPTORS;
|
||||
struct net_lro_desc *lro_arr;
|
||||
|
||||
/* Allocate the LRO descriptors structure */
|
||||
lro_arr = kzalloc(s, GFP_KERNEL);
|
||||
if (lro_arr == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
lro_mgr->lro_arr = lro_arr;
|
||||
lro_mgr->max_desc = EFX_MAX_LRO_DESCRIPTORS;
|
||||
lro_mgr->max_aggr = EFX_MAX_LRO_AGGR;
|
||||
lro_mgr->frag_align_pad = EFX_PAGE_SKB_ALIGN;
|
||||
|
||||
lro_mgr->get_skb_header = efx_lro_get_skb_hdr;
|
||||
lro_mgr->get_frag_header = efx_get_frag_hdr;
|
||||
lro_mgr->dev = efx->net_dev;
|
||||
|
||||
lro_mgr->features = LRO_F_NAPI;
|
||||
|
||||
/* We can pass packets up with the checksum intact */
|
||||
lro_mgr->ip_summed = CHECKSUM_UNNECESSARY;
|
||||
|
||||
lro_mgr->ip_summed_aggr = CHECKSUM_UNNECESSARY;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void efx_lro_fini(struct net_lro_mgr *lro_mgr)
|
||||
{
|
||||
kfree(lro_mgr->lro_arr);
|
||||
lro_mgr->lro_arr = NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* efx_init_rx_buffer_skb - create new RX buffer using skb-based allocation
|
||||
*
|
||||
* @rx_queue: Efx RX queue
|
||||
* @rx_buf: RX buffer structure to populate
|
||||
*
|
||||
* This allocates memory for a new receive buffer, maps it for DMA,
|
||||
* and populates a struct efx_rx_buffer with the relevant
|
||||
* information. Return a negative error code or 0 on success.
|
||||
*/
|
||||
static inline int efx_init_rx_buffer_skb(struct efx_rx_queue *rx_queue,
|
||||
struct efx_rx_buffer *rx_buf)
|
||||
{
|
||||
struct efx_nic *efx = rx_queue->efx;
|
||||
struct net_device *net_dev = efx->net_dev;
|
||||
int skb_len = efx->rx_buffer_len;
|
||||
|
||||
rx_buf->skb = netdev_alloc_skb(net_dev, skb_len);
|
||||
if (unlikely(!rx_buf->skb))
|
||||
return -ENOMEM;
|
||||
|
||||
/* Adjust the SKB for padding and checksum */
|
||||
skb_reserve(rx_buf->skb, NET_IP_ALIGN);
|
||||
rx_buf->len = skb_len - NET_IP_ALIGN;
|
||||
rx_buf->data = (char *)rx_buf->skb->data;
|
||||
rx_buf->skb->ip_summed = CHECKSUM_UNNECESSARY;
|
||||
|
||||
rx_buf->dma_addr = pci_map_single(efx->pci_dev,
|
||||
rx_buf->data, rx_buf->len,
|
||||
PCI_DMA_FROMDEVICE);
|
||||
|
||||
if (unlikely(pci_dma_mapping_error(rx_buf->dma_addr))) {
|
||||
dev_kfree_skb_any(rx_buf->skb);
|
||||
rx_buf->skb = NULL;
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* efx_init_rx_buffer_page - create new RX buffer using page-based allocation
|
||||
*
|
||||
* @rx_queue: Efx RX queue
|
||||
* @rx_buf: RX buffer structure to populate
|
||||
*
|
||||
* This allocates memory for a new receive buffer, maps it for DMA,
|
||||
* and populates a struct efx_rx_buffer with the relevant
|
||||
* information. Return a negative error code or 0 on success.
|
||||
*/
|
||||
static inline int efx_init_rx_buffer_page(struct efx_rx_queue *rx_queue,
|
||||
struct efx_rx_buffer *rx_buf)
|
||||
{
|
||||
struct efx_nic *efx = rx_queue->efx;
|
||||
int bytes, space, offset;
|
||||
|
||||
bytes = efx->rx_buffer_len - EFX_PAGE_IP_ALIGN;
|
||||
|
||||
/* If there is space left in the previously allocated page,
|
||||
* then use it. Otherwise allocate a new one */
|
||||
rx_buf->page = rx_queue->buf_page;
|
||||
if (rx_buf->page == NULL) {
|
||||
dma_addr_t dma_addr;
|
||||
|
||||
rx_buf->page = alloc_pages(__GFP_COLD | __GFP_COMP | GFP_ATOMIC,
|
||||
efx->rx_buffer_order);
|
||||
if (unlikely(rx_buf->page == NULL))
|
||||
return -ENOMEM;
|
||||
|
||||
dma_addr = pci_map_page(efx->pci_dev, rx_buf->page,
|
||||
0, RX_PAGE_SIZE(efx),
|
||||
PCI_DMA_FROMDEVICE);
|
||||
|
||||
if (unlikely(pci_dma_mapping_error(dma_addr))) {
|
||||
__free_pages(rx_buf->page, efx->rx_buffer_order);
|
||||
rx_buf->page = NULL;
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
rx_queue->buf_page = rx_buf->page;
|
||||
rx_queue->buf_dma_addr = dma_addr;
|
||||
rx_queue->buf_data = ((char *) page_address(rx_buf->page) +
|
||||
EFX_PAGE_IP_ALIGN);
|
||||
}
|
||||
|
||||
offset = RX_DATA_OFFSET(rx_queue->buf_data);
|
||||
rx_buf->len = bytes;
|
||||
rx_buf->dma_addr = rx_queue->buf_dma_addr + offset;
|
||||
rx_buf->data = rx_queue->buf_data;
|
||||
|
||||
/* Try to pack multiple buffers per page */
|
||||
if (efx->rx_buffer_order == 0) {
|
||||
/* The next buffer starts on the next 512 byte boundary */
|
||||
rx_queue->buf_data += ((bytes + 0x1ff) & ~0x1ff);
|
||||
offset += ((bytes + 0x1ff) & ~0x1ff);
|
||||
|
||||
space = RX_PAGE_SIZE(efx) - offset;
|
||||
if (space >= bytes) {
|
||||
/* Refs dropped on kernel releasing each skb */
|
||||
get_page(rx_queue->buf_page);
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
/* This is the final RX buffer for this page, so mark it for
|
||||
* unmapping */
|
||||
rx_queue->buf_page = NULL;
|
||||
rx_buf->unmap_addr = rx_queue->buf_dma_addr;
|
||||
|
||||
out:
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* This allocates memory for a new receive buffer, maps it for DMA,
|
||||
* and populates a struct efx_rx_buffer with the relevant
|
||||
* information.
|
||||
*/
|
||||
static inline int efx_init_rx_buffer(struct efx_rx_queue *rx_queue,
|
||||
struct efx_rx_buffer *new_rx_buf)
|
||||
{
|
||||
int rc = 0;
|
||||
|
||||
if (rx_queue->channel->rx_alloc_push_pages) {
|
||||
new_rx_buf->skb = NULL;
|
||||
rc = efx_init_rx_buffer_page(rx_queue, new_rx_buf);
|
||||
rx_queue->alloc_page_count++;
|
||||
} else {
|
||||
new_rx_buf->page = NULL;
|
||||
rc = efx_init_rx_buffer_skb(rx_queue, new_rx_buf);
|
||||
rx_queue->alloc_skb_count++;
|
||||
}
|
||||
|
||||
if (unlikely(rc < 0))
|
||||
EFX_LOG_RL(rx_queue->efx, "%s RXQ[%d] =%d\n", __func__,
|
||||
rx_queue->queue, rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static inline void efx_unmap_rx_buffer(struct efx_nic *efx,
|
||||
struct efx_rx_buffer *rx_buf)
|
||||
{
|
||||
if (rx_buf->page) {
|
||||
EFX_BUG_ON_PARANOID(rx_buf->skb);
|
||||
if (rx_buf->unmap_addr) {
|
||||
pci_unmap_page(efx->pci_dev, rx_buf->unmap_addr,
|
||||
RX_PAGE_SIZE(efx), PCI_DMA_FROMDEVICE);
|
||||
rx_buf->unmap_addr = 0;
|
||||
}
|
||||
} else if (likely(rx_buf->skb)) {
|
||||
pci_unmap_single(efx->pci_dev, rx_buf->dma_addr,
|
||||
rx_buf->len, PCI_DMA_FROMDEVICE);
|
||||
}
|
||||
}
|
||||
|
||||
static inline void efx_free_rx_buffer(struct efx_nic *efx,
|
||||
struct efx_rx_buffer *rx_buf)
|
||||
{
|
||||
if (rx_buf->page) {
|
||||
__free_pages(rx_buf->page, efx->rx_buffer_order);
|
||||
rx_buf->page = NULL;
|
||||
} else if (likely(rx_buf->skb)) {
|
||||
dev_kfree_skb_any(rx_buf->skb);
|
||||
rx_buf->skb = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static inline void efx_fini_rx_buffer(struct efx_rx_queue *rx_queue,
|
||||
struct efx_rx_buffer *rx_buf)
|
||||
{
|
||||
efx_unmap_rx_buffer(rx_queue->efx, rx_buf);
|
||||
efx_free_rx_buffer(rx_queue->efx, rx_buf);
|
||||
}
|
||||
|
||||
/**
|
||||
* efx_fast_push_rx_descriptors - push new RX descriptors quickly
|
||||
* @rx_queue: RX descriptor queue
|
||||
* @retry: Recheck the fill level
|
||||
* This will aim to fill the RX descriptor queue up to
|
||||
* @rx_queue->@fast_fill_limit. If there is insufficient atomic
|
||||
* memory to do so, the caller should retry.
|
||||
*/
|
||||
static int __efx_fast_push_rx_descriptors(struct efx_rx_queue *rx_queue,
|
||||
int retry)
|
||||
{
|
||||
struct efx_rx_buffer *rx_buf;
|
||||
unsigned fill_level, index;
|
||||
int i, space, rc = 0;
|
||||
|
||||
/* Calculate current fill level. Do this outside the lock,
|
||||
* because most of the time we'll end up not wanting to do the
|
||||
* fill anyway.
|
||||
*/
|
||||
fill_level = (rx_queue->added_count - rx_queue->removed_count);
|
||||
EFX_BUG_ON_PARANOID(fill_level >
|
||||
rx_queue->efx->type->rxd_ring_mask + 1);
|
||||
|
||||
/* Don't fill if we don't need to */
|
||||
if (fill_level >= rx_queue->fast_fill_trigger)
|
||||
return 0;
|
||||
|
||||
/* Record minimum fill level */
|
||||
if (unlikely(fill_level < rx_queue->min_fill))
|
||||
if (fill_level)
|
||||
rx_queue->min_fill = fill_level;
|
||||
|
||||
/* Acquire RX add lock. If this lock is contended, then a fast
|
||||
* fill must already be in progress (e.g. in the refill
|
||||
* tasklet), so we don't need to do anything
|
||||
*/
|
||||
if (!spin_trylock_bh(&rx_queue->add_lock))
|
||||
return -1;
|
||||
|
||||
retry:
|
||||
/* Recalculate current fill level now that we have the lock */
|
||||
fill_level = (rx_queue->added_count - rx_queue->removed_count);
|
||||
EFX_BUG_ON_PARANOID(fill_level >
|
||||
rx_queue->efx->type->rxd_ring_mask + 1);
|
||||
space = rx_queue->fast_fill_limit - fill_level;
|
||||
if (space < EFX_RX_BATCH)
|
||||
goto out_unlock;
|
||||
|
||||
EFX_TRACE(rx_queue->efx, "RX queue %d fast-filling descriptor ring from"
|
||||
" level %d to level %d using %s allocation\n",
|
||||
rx_queue->queue, fill_level, rx_queue->fast_fill_limit,
|
||||
rx_queue->channel->rx_alloc_push_pages ? "page" : "skb");
|
||||
|
||||
do {
|
||||
for (i = 0; i < EFX_RX_BATCH; ++i) {
|
||||
index = (rx_queue->added_count &
|
||||
rx_queue->efx->type->rxd_ring_mask);
|
||||
rx_buf = efx_rx_buffer(rx_queue, index);
|
||||
rc = efx_init_rx_buffer(rx_queue, rx_buf);
|
||||
if (unlikely(rc))
|
||||
goto out;
|
||||
++rx_queue->added_count;
|
||||
}
|
||||
} while ((space -= EFX_RX_BATCH) >= EFX_RX_BATCH);
|
||||
|
||||
EFX_TRACE(rx_queue->efx, "RX queue %d fast-filled descriptor ring "
|
||||
"to level %d\n", rx_queue->queue,
|
||||
rx_queue->added_count - rx_queue->removed_count);
|
||||
|
||||
out:
|
||||
/* Send write pointer to card. */
|
||||
falcon_notify_rx_desc(rx_queue);
|
||||
|
||||
/* If the fast fill is running inside from the refill tasklet, then
|
||||
* for SMP systems it may be running on a different CPU to
|
||||
* RX event processing, which means that the fill level may now be
|
||||
* out of date. */
|
||||
if (unlikely(retry && (rc == 0)))
|
||||
goto retry;
|
||||
|
||||
out_unlock:
|
||||
spin_unlock_bh(&rx_queue->add_lock);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* efx_fast_push_rx_descriptors - push new RX descriptors quickly
|
||||
* @rx_queue: RX descriptor queue
|
||||
*
|
||||
* This will aim to fill the RX descriptor queue up to
|
||||
* @rx_queue->@fast_fill_limit. If there is insufficient memory to do so,
|
||||
* it will schedule a work item to immediately continue the fast fill
|
||||
*/
|
||||
void efx_fast_push_rx_descriptors(struct efx_rx_queue *rx_queue)
|
||||
{
|
||||
int rc;
|
||||
|
||||
rc = __efx_fast_push_rx_descriptors(rx_queue, 0);
|
||||
if (unlikely(rc)) {
|
||||
/* Schedule the work item to run immediately. The hope is
|
||||
* that work is immediately pending to free some memory
|
||||
* (e.g. an RX event or TX completion)
|
||||
*/
|
||||
efx_schedule_slow_fill(rx_queue, 0);
|
||||
}
|
||||
}
|
||||
|
||||
void efx_rx_work(struct work_struct *data)
|
||||
{
|
||||
struct efx_rx_queue *rx_queue;
|
||||
int rc;
|
||||
|
||||
rx_queue = container_of(data, struct efx_rx_queue, work.work);
|
||||
|
||||
if (unlikely(!rx_queue->channel->enabled))
|
||||
return;
|
||||
|
||||
EFX_TRACE(rx_queue->efx, "RX queue %d worker thread executing on CPU "
|
||||
"%d\n", rx_queue->queue, raw_smp_processor_id());
|
||||
|
||||
++rx_queue->slow_fill_count;
|
||||
/* Push new RX descriptors, allowing at least 1 jiffy for
|
||||
* the kernel to free some more memory. */
|
||||
rc = __efx_fast_push_rx_descriptors(rx_queue, 1);
|
||||
if (rc)
|
||||
efx_schedule_slow_fill(rx_queue, 1);
|
||||
}
|
||||
|
||||
static inline void efx_rx_packet__check_len(struct efx_rx_queue *rx_queue,
|
||||
struct efx_rx_buffer *rx_buf,
|
||||
int len, int *discard,
|
||||
int *leak_packet)
|
||||
{
|
||||
struct efx_nic *efx = rx_queue->efx;
|
||||
unsigned max_len = rx_buf->len - efx->type->rx_buffer_padding;
|
||||
|
||||
if (likely(len <= max_len))
|
||||
return;
|
||||
|
||||
/* The packet must be discarded, but this is only a fatal error
|
||||
* if the caller indicated it was
|
||||
*/
|
||||
*discard = 1;
|
||||
|
||||
if ((len > rx_buf->len) && EFX_WORKAROUND_8071(efx)) {
|
||||
EFX_ERR_RL(efx, " RX queue %d seriously overlength "
|
||||
"RX event (0x%x > 0x%x+0x%x). Leaking\n",
|
||||
rx_queue->queue, len, max_len,
|
||||
efx->type->rx_buffer_padding);
|
||||
/* If this buffer was skb-allocated, then the meta
|
||||
* data at the end of the skb will be trashed. So
|
||||
* we have no choice but to leak the fragment.
|
||||
*/
|
||||
*leak_packet = (rx_buf->skb != NULL);
|
||||
efx_schedule_reset(efx, RESET_TYPE_RX_RECOVERY);
|
||||
} else {
|
||||
EFX_ERR_RL(efx, " RX queue %d overlength RX event "
|
||||
"(0x%x > 0x%x)\n", rx_queue->queue, len, max_len);
|
||||
}
|
||||
|
||||
rx_queue->channel->n_rx_overlength++;
|
||||
}
|
||||
|
||||
/* Pass a received packet up through the generic LRO stack
|
||||
*
|
||||
* Handles driverlink veto, and passes the fragment up via
|
||||
* the appropriate LRO method
|
||||
*/
|
||||
static inline void efx_rx_packet_lro(struct efx_channel *channel,
|
||||
struct efx_rx_buffer *rx_buf)
|
||||
{
|
||||
struct net_lro_mgr *lro_mgr = &channel->lro_mgr;
|
||||
void *priv = channel;
|
||||
|
||||
/* Pass the skb/page into the LRO engine */
|
||||
if (rx_buf->page) {
|
||||
struct skb_frag_struct frags;
|
||||
|
||||
frags.page = rx_buf->page;
|
||||
frags.page_offset = RX_BUF_OFFSET(rx_buf);
|
||||
frags.size = rx_buf->len;
|
||||
|
||||
lro_receive_frags(lro_mgr, &frags, rx_buf->len,
|
||||
rx_buf->len, priv, 0);
|
||||
|
||||
EFX_BUG_ON_PARANOID(rx_buf->skb);
|
||||
rx_buf->page = NULL;
|
||||
} else {
|
||||
EFX_BUG_ON_PARANOID(!rx_buf->skb);
|
||||
|
||||
lro_receive_skb(lro_mgr, rx_buf->skb, priv);
|
||||
rx_buf->skb = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/* Allocate and construct an SKB around a struct page.*/
|
||||
static inline struct sk_buff *efx_rx_mk_skb(struct efx_rx_buffer *rx_buf,
|
||||
struct efx_nic *efx,
|
||||
int hdr_len)
|
||||
{
|
||||
struct sk_buff *skb;
|
||||
|
||||
/* Allocate an SKB to store the headers */
|
||||
skb = netdev_alloc_skb(efx->net_dev, hdr_len + EFX_PAGE_SKB_ALIGN);
|
||||
if (unlikely(skb == NULL)) {
|
||||
EFX_ERR_RL(efx, "RX out of memory for skb\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
EFX_BUG_ON_PARANOID(skb_shinfo(skb)->nr_frags);
|
||||
EFX_BUG_ON_PARANOID(rx_buf->len < hdr_len);
|
||||
|
||||
skb->ip_summed = CHECKSUM_UNNECESSARY;
|
||||
skb_reserve(skb, EFX_PAGE_SKB_ALIGN);
|
||||
|
||||
skb->len = rx_buf->len;
|
||||
skb->truesize = rx_buf->len + sizeof(struct sk_buff);
|
||||
memcpy(skb->data, rx_buf->data, hdr_len);
|
||||
skb->tail += hdr_len;
|
||||
|
||||
/* Append the remaining page onto the frag list */
|
||||
if (unlikely(rx_buf->len > hdr_len)) {
|
||||
struct skb_frag_struct *frag = skb_shinfo(skb)->frags;
|
||||
frag->page = rx_buf->page;
|
||||
frag->page_offset = RX_BUF_OFFSET(rx_buf) + hdr_len;
|
||||
frag->size = skb->len - hdr_len;
|
||||
skb_shinfo(skb)->nr_frags = 1;
|
||||
skb->data_len = frag->size;
|
||||
} else {
|
||||
__free_pages(rx_buf->page, efx->rx_buffer_order);
|
||||
skb->data_len = 0;
|
||||
}
|
||||
|
||||
/* Ownership has transferred from the rx_buf to skb */
|
||||
rx_buf->page = NULL;
|
||||
|
||||
/* Move past the ethernet header */
|
||||
skb->protocol = eth_type_trans(skb, efx->net_dev);
|
||||
|
||||
return skb;
|
||||
}
|
||||
|
||||
void efx_rx_packet(struct efx_rx_queue *rx_queue, unsigned int index,
|
||||
unsigned int len, int checksummed, int discard)
|
||||
{
|
||||
struct efx_nic *efx = rx_queue->efx;
|
||||
struct efx_rx_buffer *rx_buf;
|
||||
int leak_packet = 0;
|
||||
|
||||
rx_buf = efx_rx_buffer(rx_queue, index);
|
||||
EFX_BUG_ON_PARANOID(!rx_buf->data);
|
||||
EFX_BUG_ON_PARANOID(rx_buf->skb && rx_buf->page);
|
||||
EFX_BUG_ON_PARANOID(!(rx_buf->skb || rx_buf->page));
|
||||
|
||||
/* This allows the refill path to post another buffer.
|
||||
* EFX_RXD_HEAD_ROOM ensures that the slot we are using
|
||||
* isn't overwritten yet.
|
||||
*/
|
||||
rx_queue->removed_count++;
|
||||
|
||||
/* Validate the length encoded in the event vs the descriptor pushed */
|
||||
efx_rx_packet__check_len(rx_queue, rx_buf, len,
|
||||
&discard, &leak_packet);
|
||||
|
||||
EFX_TRACE(efx, "RX queue %d received id %x at %llx+%x %s%s\n",
|
||||
rx_queue->queue, index,
|
||||
(unsigned long long)rx_buf->dma_addr, len,
|
||||
(checksummed ? " [SUMMED]" : ""),
|
||||
(discard ? " [DISCARD]" : ""));
|
||||
|
||||
/* Discard packet, if instructed to do so */
|
||||
if (unlikely(discard)) {
|
||||
if (unlikely(leak_packet))
|
||||
rx_queue->channel->n_skbuff_leaks++;
|
||||
else
|
||||
/* We haven't called efx_unmap_rx_buffer yet,
|
||||
* so fini the entire rx_buffer here */
|
||||
efx_fini_rx_buffer(rx_queue, rx_buf);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Release card resources - assumes all RX buffers consumed in-order
|
||||
* per RX queue
|
||||
*/
|
||||
efx_unmap_rx_buffer(efx, rx_buf);
|
||||
|
||||
/* Prefetch nice and early so data will (hopefully) be in cache by
|
||||
* the time we look at it.
|
||||
*/
|
||||
prefetch(rx_buf->data);
|
||||
|
||||
/* Pipeline receives so that we give time for packet headers to be
|
||||
* prefetched into cache.
|
||||
*/
|
||||
rx_buf->len = len;
|
||||
if (rx_queue->channel->rx_pkt)
|
||||
__efx_rx_packet(rx_queue->channel,
|
||||
rx_queue->channel->rx_pkt,
|
||||
rx_queue->channel->rx_pkt_csummed);
|
||||
rx_queue->channel->rx_pkt = rx_buf;
|
||||
rx_queue->channel->rx_pkt_csummed = checksummed;
|
||||
}
|
||||
|
||||
/* Handle a received packet. Second half: Touches packet payload. */
|
||||
void __efx_rx_packet(struct efx_channel *channel,
|
||||
struct efx_rx_buffer *rx_buf, int checksummed)
|
||||
{
|
||||
struct efx_nic *efx = channel->efx;
|
||||
struct sk_buff *skb;
|
||||
int lro = efx->net_dev->features & NETIF_F_LRO;
|
||||
|
||||
if (rx_buf->skb) {
|
||||
prefetch(skb_shinfo(rx_buf->skb));
|
||||
|
||||
skb_put(rx_buf->skb, rx_buf->len);
|
||||
|
||||
/* Move past the ethernet header. rx_buf->data still points
|
||||
* at the ethernet header */
|
||||
rx_buf->skb->protocol = eth_type_trans(rx_buf->skb,
|
||||
efx->net_dev);
|
||||
}
|
||||
|
||||
/* Both our generic-LRO and SFC-SSR support skb and page based
|
||||
* allocation, but neither support switching from one to the
|
||||
* other on the fly. If we spot that the allocation mode has
|
||||
* changed, then flush the LRO state.
|
||||
*/
|
||||
if (unlikely(channel->rx_alloc_pop_pages != (rx_buf->page != NULL))) {
|
||||
efx_flush_lro(channel);
|
||||
channel->rx_alloc_pop_pages = (rx_buf->page != NULL);
|
||||
}
|
||||
if (likely(checksummed && lro)) {
|
||||
efx_rx_packet_lro(channel, rx_buf);
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* Form an skb if required */
|
||||
if (rx_buf->page) {
|
||||
int hdr_len = min(rx_buf->len, EFX_SKB_HEADERS);
|
||||
skb = efx_rx_mk_skb(rx_buf, efx, hdr_len);
|
||||
if (unlikely(skb == NULL)) {
|
||||
efx_free_rx_buffer(efx, rx_buf);
|
||||
goto done;
|
||||
}
|
||||
} else {
|
||||
/* We now own the SKB */
|
||||
skb = rx_buf->skb;
|
||||
rx_buf->skb = NULL;
|
||||
}
|
||||
|
||||
EFX_BUG_ON_PARANOID(rx_buf->page);
|
||||
EFX_BUG_ON_PARANOID(rx_buf->skb);
|
||||
EFX_BUG_ON_PARANOID(!skb);
|
||||
|
||||
/* Set the SKB flags */
|
||||
if (unlikely(!checksummed || !efx->rx_checksum_enabled))
|
||||
skb->ip_summed = CHECKSUM_NONE;
|
||||
|
||||
/* Pass the packet up */
|
||||
netif_receive_skb(skb);
|
||||
|
||||
/* Update allocation strategy method */
|
||||
channel->rx_alloc_level += RX_ALLOC_FACTOR_SKB;
|
||||
|
||||
/* fall-thru */
|
||||
done:
|
||||
efx->net_dev->last_rx = jiffies;
|
||||
}
|
||||
|
||||
void efx_rx_strategy(struct efx_channel *channel)
|
||||
{
|
||||
enum efx_rx_alloc_method method = rx_alloc_method;
|
||||
|
||||
/* Only makes sense to use page based allocation if LRO is enabled */
|
||||
if (!(channel->efx->net_dev->features & NETIF_F_LRO)) {
|
||||
method = RX_ALLOC_METHOD_SKB;
|
||||
} else if (method == RX_ALLOC_METHOD_AUTO) {
|
||||
/* Constrain the rx_alloc_level */
|
||||
if (channel->rx_alloc_level < 0)
|
||||
channel->rx_alloc_level = 0;
|
||||
else if (channel->rx_alloc_level > RX_ALLOC_LEVEL_MAX)
|
||||
channel->rx_alloc_level = RX_ALLOC_LEVEL_MAX;
|
||||
|
||||
/* Decide on the allocation method */
|
||||
method = ((channel->rx_alloc_level > RX_ALLOC_LEVEL_LRO) ?
|
||||
RX_ALLOC_METHOD_PAGE : RX_ALLOC_METHOD_SKB);
|
||||
}
|
||||
|
||||
/* Push the option */
|
||||
channel->rx_alloc_push_pages = (method == RX_ALLOC_METHOD_PAGE);
|
||||
}
|
||||
|
||||
int efx_probe_rx_queue(struct efx_rx_queue *rx_queue)
|
||||
{
|
||||
struct efx_nic *efx = rx_queue->efx;
|
||||
unsigned int rxq_size;
|
||||
int rc;
|
||||
|
||||
EFX_LOG(efx, "creating RX queue %d\n", rx_queue->queue);
|
||||
|
||||
/* Allocate RX buffers */
|
||||
rxq_size = (efx->type->rxd_ring_mask + 1) * sizeof(*rx_queue->buffer);
|
||||
rx_queue->buffer = kzalloc(rxq_size, GFP_KERNEL);
|
||||
if (!rx_queue->buffer) {
|
||||
rc = -ENOMEM;
|
||||
goto fail1;
|
||||
}
|
||||
|
||||
rc = falcon_probe_rx(rx_queue);
|
||||
if (rc)
|
||||
goto fail2;
|
||||
|
||||
return 0;
|
||||
|
||||
fail2:
|
||||
kfree(rx_queue->buffer);
|
||||
rx_queue->buffer = NULL;
|
||||
fail1:
|
||||
rx_queue->used = 0;
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
int efx_init_rx_queue(struct efx_rx_queue *rx_queue)
|
||||
{
|
||||
struct efx_nic *efx = rx_queue->efx;
|
||||
unsigned int max_fill, trigger, limit;
|
||||
|
||||
EFX_LOG(rx_queue->efx, "initialising RX queue %d\n", rx_queue->queue);
|
||||
|
||||
/* Initialise ptr fields */
|
||||
rx_queue->added_count = 0;
|
||||
rx_queue->notified_count = 0;
|
||||
rx_queue->removed_count = 0;
|
||||
rx_queue->min_fill = -1U;
|
||||
rx_queue->min_overfill = -1U;
|
||||
|
||||
/* Initialise limit fields */
|
||||
max_fill = efx->type->rxd_ring_mask + 1 - EFX_RXD_HEAD_ROOM;
|
||||
trigger = max_fill * min(rx_refill_threshold, 100U) / 100U;
|
||||
limit = max_fill * min(rx_refill_limit, 100U) / 100U;
|
||||
|
||||
rx_queue->max_fill = max_fill;
|
||||
rx_queue->fast_fill_trigger = trigger;
|
||||
rx_queue->fast_fill_limit = limit;
|
||||
|
||||
/* Set up RX descriptor ring */
|
||||
return falcon_init_rx(rx_queue);
|
||||
}
|
||||
|
||||
void efx_fini_rx_queue(struct efx_rx_queue *rx_queue)
|
||||
{
|
||||
int i;
|
||||
struct efx_rx_buffer *rx_buf;
|
||||
|
||||
EFX_LOG(rx_queue->efx, "shutting down RX queue %d\n", rx_queue->queue);
|
||||
|
||||
falcon_fini_rx(rx_queue);
|
||||
|
||||
/* Release RX buffers NB start at index 0 not current HW ptr */
|
||||
if (rx_queue->buffer) {
|
||||
for (i = 0; i <= rx_queue->efx->type->rxd_ring_mask; i++) {
|
||||
rx_buf = efx_rx_buffer(rx_queue, i);
|
||||
efx_fini_rx_buffer(rx_queue, rx_buf);
|
||||
}
|
||||
}
|
||||
|
||||
/* For a page that is part-way through splitting into RX buffers */
|
||||
if (rx_queue->buf_page != NULL) {
|
||||
pci_unmap_page(rx_queue->efx->pci_dev, rx_queue->buf_dma_addr,
|
||||
RX_PAGE_SIZE(rx_queue->efx), PCI_DMA_FROMDEVICE);
|
||||
__free_pages(rx_queue->buf_page,
|
||||
rx_queue->efx->rx_buffer_order);
|
||||
rx_queue->buf_page = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void efx_remove_rx_queue(struct efx_rx_queue *rx_queue)
|
||||
{
|
||||
EFX_LOG(rx_queue->efx, "destroying RX queue %d\n", rx_queue->queue);
|
||||
|
||||
falcon_remove_rx(rx_queue);
|
||||
|
||||
kfree(rx_queue->buffer);
|
||||
rx_queue->buffer = NULL;
|
||||
rx_queue->used = 0;
|
||||
}
|
||||
|
||||
void efx_flush_lro(struct efx_channel *channel)
|
||||
{
|
||||
lro_flush_all(&channel->lro_mgr);
|
||||
}
|
||||
|
||||
|
||||
module_param(rx_alloc_method, int, 0644);
|
||||
MODULE_PARM_DESC(rx_alloc_method, "Allocation method used for RX buffers");
|
||||
|
||||
module_param(rx_refill_threshold, uint, 0444);
|
||||
MODULE_PARM_DESC(rx_refill_threshold,
|
||||
"RX descriptor ring fast/slow fill threshold (%)");
|
||||
|
29
drivers/net/sfc/rx.h
Normal file
29
drivers/net/sfc/rx.h
Normal file
@ -0,0 +1,29 @@
|
||||
/****************************************************************************
|
||||
* Driver for Solarflare Solarstorm network controllers and boards
|
||||
* Copyright 2006 Solarflare Communications Inc.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 as published
|
||||
* by the Free Software Foundation, incorporated herein by reference.
|
||||
*/
|
||||
|
||||
#ifndef EFX_RX_H
|
||||
#define EFX_RX_H
|
||||
|
||||
#include "net_driver.h"
|
||||
|
||||
int efx_probe_rx_queue(struct efx_rx_queue *rx_queue);
|
||||
void efx_remove_rx_queue(struct efx_rx_queue *rx_queue);
|
||||
int efx_init_rx_queue(struct efx_rx_queue *rx_queue);
|
||||
void efx_fini_rx_queue(struct efx_rx_queue *rx_queue);
|
||||
|
||||
int efx_lro_init(struct net_lro_mgr *lro_mgr, struct efx_nic *efx);
|
||||
void efx_lro_fini(struct net_lro_mgr *lro_mgr);
|
||||
void efx_flush_lro(struct efx_channel *channel);
|
||||
void efx_rx_strategy(struct efx_channel *channel);
|
||||
void efx_fast_push_rx_descriptors(struct efx_rx_queue *rx_queue);
|
||||
void efx_rx_work(struct work_struct *data);
|
||||
void __efx_rx_packet(struct efx_channel *channel,
|
||||
struct efx_rx_buffer *rx_buf, int checksummed);
|
||||
|
||||
#endif /* EFX_RX_H */
|
252
drivers/net/sfc/sfe4001.c
Normal file
252
drivers/net/sfc/sfe4001.c
Normal file
@ -0,0 +1,252 @@
|
||||
/****************************************************************************
|
||||
* Driver for Solarflare Solarstorm network controllers and boards
|
||||
* Copyright 2007 Solarflare Communications Inc.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 as published
|
||||
* by the Free Software Foundation, incorporated herein by reference.
|
||||
*/
|
||||
|
||||
/*****************************************************************************
|
||||
* Support for the SFE4001 NIC: driver code for the PCA9539 I/O expander that
|
||||
* controls the PHY power rails, and for the MAX6647 temp. sensor used to check
|
||||
* the PHY
|
||||
*/
|
||||
#include <linux/delay.h>
|
||||
#include "efx.h"
|
||||
#include "phy.h"
|
||||
#include "boards.h"
|
||||
#include "falcon.h"
|
||||
#include "falcon_hwdefs.h"
|
||||
#include "mac.h"
|
||||
|
||||
/**************************************************************************
|
||||
*
|
||||
* I2C IO Expander device
|
||||
*
|
||||
**************************************************************************/
|
||||
#define PCA9539 0x74
|
||||
|
||||
#define P0_IN 0x00
|
||||
#define P0_OUT 0x02
|
||||
#define P0_INVERT 0x04
|
||||
#define P0_CONFIG 0x06
|
||||
|
||||
#define P0_EN_1V0X_LBN 0
|
||||
#define P0_EN_1V0X_WIDTH 1
|
||||
#define P0_EN_1V2_LBN 1
|
||||
#define P0_EN_1V2_WIDTH 1
|
||||
#define P0_EN_2V5_LBN 2
|
||||
#define P0_EN_2V5_WIDTH 1
|
||||
#define P0_EN_3V3X_LBN 3
|
||||
#define P0_EN_3V3X_WIDTH 1
|
||||
#define P0_EN_5V_LBN 4
|
||||
#define P0_EN_5V_WIDTH 1
|
||||
#define P0_SHORTEN_JTAG_LBN 5
|
||||
#define P0_SHORTEN_JTAG_WIDTH 1
|
||||
#define P0_X_TRST_LBN 6
|
||||
#define P0_X_TRST_WIDTH 1
|
||||
#define P0_DSP_RESET_LBN 7
|
||||
#define P0_DSP_RESET_WIDTH 1
|
||||
|
||||
#define P1_IN 0x01
|
||||
#define P1_OUT 0x03
|
||||
#define P1_INVERT 0x05
|
||||
#define P1_CONFIG 0x07
|
||||
|
||||
#define P1_AFE_PWD_LBN 0
|
||||
#define P1_AFE_PWD_WIDTH 1
|
||||
#define P1_DSP_PWD25_LBN 1
|
||||
#define P1_DSP_PWD25_WIDTH 1
|
||||
#define P1_RESERVED_LBN 2
|
||||
#define P1_RESERVED_WIDTH 2
|
||||
#define P1_SPARE_LBN 4
|
||||
#define P1_SPARE_WIDTH 4
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
*
|
||||
* Temperature Sensor
|
||||
*
|
||||
**************************************************************************/
|
||||
#define MAX6647 0x4e
|
||||
|
||||
#define RLTS 0x00
|
||||
#define RLTE 0x01
|
||||
#define RSL 0x02
|
||||
#define RCL 0x03
|
||||
#define RCRA 0x04
|
||||
#define RLHN 0x05
|
||||
#define RLLI 0x06
|
||||
#define RRHI 0x07
|
||||
#define RRLS 0x08
|
||||
#define WCRW 0x0a
|
||||
#define WLHO 0x0b
|
||||
#define WRHA 0x0c
|
||||
#define WRLN 0x0e
|
||||
#define OSHT 0x0f
|
||||
#define REET 0x10
|
||||
#define RIET 0x11
|
||||
#define RWOE 0x19
|
||||
#define RWOI 0x20
|
||||
#define HYS 0x21
|
||||
#define QUEUE 0x22
|
||||
#define MFID 0xfe
|
||||
#define REVID 0xff
|
||||
|
||||
/* Status bits */
|
||||
#define MAX6647_BUSY (1 << 7) /* ADC is converting */
|
||||
#define MAX6647_LHIGH (1 << 6) /* Local high temp. alarm */
|
||||
#define MAX6647_LLOW (1 << 5) /* Local low temp. alarm */
|
||||
#define MAX6647_RHIGH (1 << 4) /* Remote high temp. alarm */
|
||||
#define MAX6647_RLOW (1 << 3) /* Remote low temp. alarm */
|
||||
#define MAX6647_FAULT (1 << 2) /* DXN/DXP short/open circuit */
|
||||
#define MAX6647_EOT (1 << 1) /* Remote junction overtemp. */
|
||||
#define MAX6647_IOT (1 << 0) /* Local junction overtemp. */
|
||||
|
||||
static const u8 xgphy_max_temperature = 90;
|
||||
|
||||
void sfe4001_poweroff(struct efx_nic *efx)
|
||||
{
|
||||
struct efx_i2c_interface *i2c = &efx->i2c;
|
||||
|
||||
u8 cfg, out, in;
|
||||
|
||||
EFX_INFO(efx, "%s\n", __func__);
|
||||
|
||||
/* Turn off all power rails */
|
||||
out = 0xff;
|
||||
(void) efx_i2c_write(i2c, PCA9539, P0_OUT, &out, 1);
|
||||
|
||||
/* Disable port 1 outputs on IO expander */
|
||||
cfg = 0xff;
|
||||
(void) efx_i2c_write(i2c, PCA9539, P1_CONFIG, &cfg, 1);
|
||||
|
||||
/* Disable port 0 outputs on IO expander */
|
||||
cfg = 0xff;
|
||||
(void) efx_i2c_write(i2c, PCA9539, P0_CONFIG, &cfg, 1);
|
||||
|
||||
/* Clear any over-temperature alert */
|
||||
(void) efx_i2c_read(i2c, MAX6647, RSL, &in, 1);
|
||||
}
|
||||
|
||||
/* This board uses an I2C expander to provider power to the PHY, which needs to
|
||||
* be turned on before the PHY can be used.
|
||||
* Context: Process context, rtnl lock held
|
||||
*/
|
||||
int sfe4001_poweron(struct efx_nic *efx)
|
||||
{
|
||||
struct efx_i2c_interface *i2c = &efx->i2c;
|
||||
unsigned int count;
|
||||
int rc;
|
||||
u8 out, in, cfg;
|
||||
efx_dword_t reg;
|
||||
|
||||
/* 10Xpress has fixed-function LED pins, so there is no board-specific
|
||||
* blink code. */
|
||||
efx->board_info.blink = tenxpress_phy_blink;
|
||||
|
||||
/* Ensure that XGXS and XAUI SerDes are held in reset */
|
||||
EFX_POPULATE_DWORD_7(reg, XX_PWRDNA_EN, 1,
|
||||
XX_PWRDNB_EN, 1,
|
||||
XX_RSTPLLAB_EN, 1,
|
||||
XX_RESETA_EN, 1,
|
||||
XX_RESETB_EN, 1,
|
||||
XX_RSTXGXSRX_EN, 1,
|
||||
XX_RSTXGXSTX_EN, 1);
|
||||
falcon_xmac_writel(efx, ®, XX_PWR_RST_REG_MAC);
|
||||
udelay(10);
|
||||
|
||||
/* Set DSP over-temperature alert threshold */
|
||||
EFX_INFO(efx, "DSP cut-out at %dC\n", xgphy_max_temperature);
|
||||
rc = efx_i2c_write(i2c, MAX6647, WLHO,
|
||||
&xgphy_max_temperature, 1);
|
||||
if (rc)
|
||||
goto fail1;
|
||||
|
||||
/* Read it back and verify */
|
||||
rc = efx_i2c_read(i2c, MAX6647, RLHN, &in, 1);
|
||||
if (rc)
|
||||
goto fail1;
|
||||
if (in != xgphy_max_temperature) {
|
||||
rc = -EFAULT;
|
||||
goto fail1;
|
||||
}
|
||||
|
||||
/* Clear any previous over-temperature alert */
|
||||
rc = efx_i2c_read(i2c, MAX6647, RSL, &in, 1);
|
||||
if (rc)
|
||||
goto fail1;
|
||||
|
||||
/* Enable port 0 and port 1 outputs on IO expander */
|
||||
cfg = 0x00;
|
||||
rc = efx_i2c_write(i2c, PCA9539, P0_CONFIG, &cfg, 1);
|
||||
if (rc)
|
||||
goto fail1;
|
||||
cfg = 0xff & ~(1 << P1_SPARE_LBN);
|
||||
rc = efx_i2c_write(i2c, PCA9539, P1_CONFIG, &cfg, 1);
|
||||
if (rc)
|
||||
goto fail2;
|
||||
|
||||
/* Turn all power off then wait 1 sec. This ensures PHY is reset */
|
||||
out = 0xff & ~((0 << P0_EN_1V2_LBN) | (0 << P0_EN_2V5_LBN) |
|
||||
(0 << P0_EN_3V3X_LBN) | (0 << P0_EN_5V_LBN) |
|
||||
(0 << P0_EN_1V0X_LBN));
|
||||
rc = efx_i2c_write(i2c, PCA9539, P0_OUT, &out, 1);
|
||||
if (rc)
|
||||
goto fail3;
|
||||
|
||||
schedule_timeout_uninterruptible(HZ);
|
||||
count = 0;
|
||||
do {
|
||||
/* Turn on 1.2V, 2.5V, 3.3V and 5V power rails */
|
||||
out = 0xff & ~((1 << P0_EN_1V2_LBN) | (1 << P0_EN_2V5_LBN) |
|
||||
(1 << P0_EN_3V3X_LBN) | (1 << P0_EN_5V_LBN) |
|
||||
(1 << P0_X_TRST_LBN));
|
||||
|
||||
rc = efx_i2c_write(i2c, PCA9539, P0_OUT, &out, 1);
|
||||
if (rc)
|
||||
goto fail3;
|
||||
msleep(10);
|
||||
|
||||
/* Turn on 1V power rail */
|
||||
out &= ~(1 << P0_EN_1V0X_LBN);
|
||||
rc = efx_i2c_write(i2c, PCA9539, P0_OUT, &out, 1);
|
||||
if (rc)
|
||||
goto fail3;
|
||||
|
||||
EFX_INFO(efx, "waiting for power (attempt %d)...\n", count);
|
||||
|
||||
schedule_timeout_uninterruptible(HZ);
|
||||
|
||||
/* Check DSP is powered */
|
||||
rc = efx_i2c_read(i2c, PCA9539, P1_IN, &in, 1);
|
||||
if (rc)
|
||||
goto fail3;
|
||||
if (in & (1 << P1_AFE_PWD_LBN))
|
||||
goto done;
|
||||
|
||||
} while (++count < 20);
|
||||
|
||||
EFX_INFO(efx, "timed out waiting for power\n");
|
||||
rc = -ETIMEDOUT;
|
||||
goto fail3;
|
||||
|
||||
done:
|
||||
EFX_INFO(efx, "PHY is powered on\n");
|
||||
return 0;
|
||||
|
||||
fail3:
|
||||
/* Turn off all power rails */
|
||||
out = 0xff;
|
||||
(void) efx_i2c_write(i2c, PCA9539, P0_OUT, &out, 1);
|
||||
/* Disable port 1 outputs on IO expander */
|
||||
out = 0xff;
|
||||
(void) efx_i2c_write(i2c, PCA9539, P1_CONFIG, &out, 1);
|
||||
fail2:
|
||||
/* Disable port 0 outputs on IO expander */
|
||||
out = 0xff;
|
||||
(void) efx_i2c_write(i2c, PCA9539, P0_CONFIG, &out, 1);
|
||||
fail1:
|
||||
return rc;
|
||||
}
|
71
drivers/net/sfc/spi.h
Normal file
71
drivers/net/sfc/spi.h
Normal file
@ -0,0 +1,71 @@
|
||||
/****************************************************************************
|
||||
* Driver for Solarflare Solarstorm network controllers and boards
|
||||
* Copyright 2005 Fen Systems Ltd.
|
||||
* Copyright 2006 Solarflare Communications Inc.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 as published
|
||||
* by the Free Software Foundation, incorporated herein by reference.
|
||||
*/
|
||||
|
||||
#ifndef EFX_SPI_H
|
||||
#define EFX_SPI_H
|
||||
|
||||
#include "net_driver.h"
|
||||
|
||||
/**************************************************************************
|
||||
*
|
||||
* Basic SPI command set and bit definitions
|
||||
*
|
||||
*************************************************************************/
|
||||
|
||||
/*
|
||||
* Commands common to all known devices.
|
||||
*
|
||||
*/
|
||||
|
||||
/* Write status register */
|
||||
#define SPI_WRSR 0x01
|
||||
|
||||
/* Write data to memory array */
|
||||
#define SPI_WRITE 0x02
|
||||
|
||||
/* Read data from memory array */
|
||||
#define SPI_READ 0x03
|
||||
|
||||
/* Reset write enable latch */
|
||||
#define SPI_WRDI 0x04
|
||||
|
||||
/* Read status register */
|
||||
#define SPI_RDSR 0x05
|
||||
|
||||
/* Set write enable latch */
|
||||
#define SPI_WREN 0x06
|
||||
|
||||
/* SST: Enable write to status register */
|
||||
#define SPI_SST_EWSR 0x50
|
||||
|
||||
/*
|
||||
* Status register bits. Not all bits are supported on all devices.
|
||||
*
|
||||
*/
|
||||
|
||||
/* Write-protect pin enabled */
|
||||
#define SPI_STATUS_WPEN 0x80
|
||||
|
||||
/* Block protection bit 2 */
|
||||
#define SPI_STATUS_BP2 0x10
|
||||
|
||||
/* Block protection bit 1 */
|
||||
#define SPI_STATUS_BP1 0x08
|
||||
|
||||
/* Block protection bit 0 */
|
||||
#define SPI_STATUS_BP0 0x04
|
||||
|
||||
/* State of the write enable latch */
|
||||
#define SPI_STATUS_WEN 0x02
|
||||
|
||||
/* Device busy flag */
|
||||
#define SPI_STATUS_NRDY 0x01
|
||||
|
||||
#endif /* EFX_SPI_H */
|
434
drivers/net/sfc/tenxpress.c
Normal file
434
drivers/net/sfc/tenxpress.c
Normal file
@ -0,0 +1,434 @@
|
||||
/****************************************************************************
|
||||
* Driver for Solarflare 802.3an compliant PHY
|
||||
* Copyright 2007 Solarflare Communications Inc.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 as published
|
||||
* by the Free Software Foundation, incorporated herein by reference.
|
||||
*/
|
||||
|
||||
#include <linux/delay.h>
|
||||
#include <linux/seq_file.h>
|
||||
#include "efx.h"
|
||||
#include "gmii.h"
|
||||
#include "mdio_10g.h"
|
||||
#include "falcon.h"
|
||||
#include "phy.h"
|
||||
#include "falcon_hwdefs.h"
|
||||
#include "boards.h"
|
||||
#include "mac.h"
|
||||
|
||||
/* We expect these MMDs to be in the package */
|
||||
/* AN not here as mdio_check_mmds() requires STAT2 support */
|
||||
#define TENXPRESS_REQUIRED_DEVS (MDIO_MMDREG_DEVS0_PMAPMD | \
|
||||
MDIO_MMDREG_DEVS0_PCS | \
|
||||
MDIO_MMDREG_DEVS0_PHYXS)
|
||||
|
||||
/* We complain if we fail to see the link partner as 10G capable this many
|
||||
* times in a row (must be > 1 as sampling the autoneg. registers is racy)
|
||||
*/
|
||||
#define MAX_BAD_LP_TRIES (5)
|
||||
|
||||
/* Extended control register */
|
||||
#define PMA_PMD_XCONTROL_REG 0xc000
|
||||
#define PMA_PMD_LNPGA_POWERDOWN_LBN 8
|
||||
#define PMA_PMD_LNPGA_POWERDOWN_WIDTH 1
|
||||
|
||||
/* extended status register */
|
||||
#define PMA_PMD_XSTATUS_REG 0xc001
|
||||
#define PMA_PMD_XSTAT_FLP_LBN (12)
|
||||
|
||||
/* LED control register */
|
||||
#define PMA_PMD_LED_CTRL_REG (0xc007)
|
||||
#define PMA_PMA_LED_ACTIVITY_LBN (3)
|
||||
|
||||
/* LED function override register */
|
||||
#define PMA_PMD_LED_OVERR_REG (0xc009)
|
||||
/* Bit positions for different LEDs (there are more but not wired on SFE4001)*/
|
||||
#define PMA_PMD_LED_LINK_LBN (0)
|
||||
#define PMA_PMD_LED_SPEED_LBN (2)
|
||||
#define PMA_PMD_LED_TX_LBN (4)
|
||||
#define PMA_PMD_LED_RX_LBN (6)
|
||||
/* Override settings */
|
||||
#define PMA_PMD_LED_AUTO (0) /* H/W control */
|
||||
#define PMA_PMD_LED_ON (1)
|
||||
#define PMA_PMD_LED_OFF (2)
|
||||
#define PMA_PMD_LED_FLASH (3)
|
||||
/* All LEDs under hardware control */
|
||||
#define PMA_PMD_LED_FULL_AUTO (0)
|
||||
/* Green and Amber under hardware control, Red off */
|
||||
#define PMA_PMD_LED_DEFAULT (PMA_PMD_LED_OFF << PMA_PMD_LED_RX_LBN)
|
||||
|
||||
|
||||
/* Self test (BIST) control register */
|
||||
#define PMA_PMD_BIST_CTRL_REG (0xc014)
|
||||
#define PMA_PMD_BIST_BER_LBN (2) /* Run BER test */
|
||||
#define PMA_PMD_BIST_CONT_LBN (1) /* Run continuous BIST until cleared */
|
||||
#define PMA_PMD_BIST_SINGLE_LBN (0) /* Run 1 BIST iteration (self clears) */
|
||||
/* Self test status register */
|
||||
#define PMA_PMD_BIST_STAT_REG (0xc015)
|
||||
#define PMA_PMD_BIST_ENX_LBN (3)
|
||||
#define PMA_PMD_BIST_PMA_LBN (2)
|
||||
#define PMA_PMD_BIST_RXD_LBN (1)
|
||||
#define PMA_PMD_BIST_AFE_LBN (0)
|
||||
|
||||
#define BIST_MAX_DELAY (1000)
|
||||
#define BIST_POLL_DELAY (10)
|
||||
|
||||
/* Misc register defines */
|
||||
#define PCS_CLOCK_CTRL_REG 0xd801
|
||||
#define PLL312_RST_N_LBN 2
|
||||
|
||||
#define PCS_SOFT_RST2_REG 0xd806
|
||||
#define SERDES_RST_N_LBN 13
|
||||
#define XGXS_RST_N_LBN 12
|
||||
|
||||
#define PCS_TEST_SELECT_REG 0xd807 /* PRM 10.5.8 */
|
||||
#define CLK312_EN_LBN 3
|
||||
|
||||
/* Boot status register */
|
||||
#define PCS_BOOT_STATUS_REG (0xd000)
|
||||
#define PCS_BOOT_FATAL_ERR_LBN (0)
|
||||
#define PCS_BOOT_PROGRESS_LBN (1)
|
||||
#define PCS_BOOT_PROGRESS_WIDTH (2)
|
||||
#define PCS_BOOT_COMPLETE_LBN (3)
|
||||
#define PCS_BOOT_MAX_DELAY (100)
|
||||
#define PCS_BOOT_POLL_DELAY (10)
|
||||
|
||||
/* Time to wait between powering down the LNPGA and turning off the power
|
||||
* rails */
|
||||
#define LNPGA_PDOWN_WAIT (HZ / 5)
|
||||
|
||||
static int crc_error_reset_threshold = 100;
|
||||
module_param(crc_error_reset_threshold, int, 0644);
|
||||
MODULE_PARM_DESC(crc_error_reset_threshold,
|
||||
"Max number of CRC errors before XAUI reset");
|
||||
|
||||
struct tenxpress_phy_data {
|
||||
enum tenxpress_state state;
|
||||
atomic_t bad_crc_count;
|
||||
int bad_lp_tries;
|
||||
};
|
||||
|
||||
static int tenxpress_state_is(struct efx_nic *efx, int state)
|
||||
{
|
||||
struct tenxpress_phy_data *phy_data = efx->phy_data;
|
||||
return (phy_data != NULL) && (state == phy_data->state);
|
||||
}
|
||||
|
||||
void tenxpress_set_state(struct efx_nic *efx,
|
||||
enum tenxpress_state state)
|
||||
{
|
||||
struct tenxpress_phy_data *phy_data = efx->phy_data;
|
||||
if (phy_data != NULL)
|
||||
phy_data->state = state;
|
||||
}
|
||||
|
||||
void tenxpress_crc_err(struct efx_nic *efx)
|
||||
{
|
||||
struct tenxpress_phy_data *phy_data = efx->phy_data;
|
||||
if (phy_data != NULL)
|
||||
atomic_inc(&phy_data->bad_crc_count);
|
||||
}
|
||||
|
||||
/* Check that the C166 has booted successfully */
|
||||
static int tenxpress_phy_check(struct efx_nic *efx)
|
||||
{
|
||||
int phy_id = efx->mii.phy_id;
|
||||
int count = PCS_BOOT_MAX_DELAY / PCS_BOOT_POLL_DELAY;
|
||||
int boot_stat;
|
||||
|
||||
/* Wait for the boot to complete (or not) */
|
||||
while (count) {
|
||||
boot_stat = mdio_clause45_read(efx, phy_id,
|
||||
MDIO_MMD_PCS,
|
||||
PCS_BOOT_STATUS_REG);
|
||||
if (boot_stat & (1 << PCS_BOOT_COMPLETE_LBN))
|
||||
break;
|
||||
count--;
|
||||
udelay(PCS_BOOT_POLL_DELAY);
|
||||
}
|
||||
|
||||
if (!count) {
|
||||
EFX_ERR(efx, "%s: PHY boot timed out. Last status "
|
||||
"%x\n", __func__,
|
||||
(boot_stat >> PCS_BOOT_PROGRESS_LBN) &
|
||||
((1 << PCS_BOOT_PROGRESS_WIDTH) - 1));
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void tenxpress_reset_xaui(struct efx_nic *efx);
|
||||
|
||||
static int tenxpress_init(struct efx_nic *efx)
|
||||
{
|
||||
int rc, reg;
|
||||
|
||||
/* Turn on the clock */
|
||||
reg = (1 << CLK312_EN_LBN);
|
||||
mdio_clause45_write(efx, efx->mii.phy_id,
|
||||
MDIO_MMD_PCS, PCS_TEST_SELECT_REG, reg);
|
||||
|
||||
rc = tenxpress_phy_check(efx);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
|
||||
/* Set the LEDs up as: Green = Link, Amber = Link/Act, Red = Off */
|
||||
reg = mdio_clause45_read(efx, efx->mii.phy_id,
|
||||
MDIO_MMD_PMAPMD, PMA_PMD_LED_CTRL_REG);
|
||||
reg |= (1 << PMA_PMA_LED_ACTIVITY_LBN);
|
||||
mdio_clause45_write(efx, efx->mii.phy_id, MDIO_MMD_PMAPMD,
|
||||
PMA_PMD_LED_CTRL_REG, reg);
|
||||
|
||||
reg = PMA_PMD_LED_DEFAULT;
|
||||
mdio_clause45_write(efx, efx->mii.phy_id, MDIO_MMD_PMAPMD,
|
||||
PMA_PMD_LED_OVERR_REG, reg);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int tenxpress_phy_init(struct efx_nic *efx)
|
||||
{
|
||||
struct tenxpress_phy_data *phy_data;
|
||||
int rc = 0;
|
||||
|
||||
phy_data = kzalloc(sizeof(*phy_data), GFP_KERNEL);
|
||||
efx->phy_data = phy_data;
|
||||
|
||||
tenxpress_set_state(efx, TENXPRESS_STATUS_NORMAL);
|
||||
|
||||
rc = mdio_clause45_wait_reset_mmds(efx,
|
||||
TENXPRESS_REQUIRED_DEVS);
|
||||
if (rc < 0)
|
||||
goto fail;
|
||||
|
||||
rc = mdio_clause45_check_mmds(efx, TENXPRESS_REQUIRED_DEVS, 0);
|
||||
if (rc < 0)
|
||||
goto fail;
|
||||
|
||||
rc = tenxpress_init(efx);
|
||||
if (rc < 0)
|
||||
goto fail;
|
||||
|
||||
schedule_timeout_uninterruptible(HZ / 5); /* 200ms */
|
||||
|
||||
/* Let XGXS and SerDes out of reset and resets 10XPress */
|
||||
falcon_reset_xaui(efx);
|
||||
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
kfree(efx->phy_data);
|
||||
efx->phy_data = NULL;
|
||||
return rc;
|
||||
}
|
||||
|
||||
static void tenxpress_set_bad_lp(struct efx_nic *efx, int bad_lp)
|
||||
{
|
||||
struct tenxpress_phy_data *pd = efx->phy_data;
|
||||
int reg;
|
||||
|
||||
/* Nothing to do if all is well and was previously so. */
|
||||
if (!(bad_lp || pd->bad_lp_tries))
|
||||
return;
|
||||
|
||||
reg = mdio_clause45_read(efx, efx->mii.phy_id,
|
||||
MDIO_MMD_PMAPMD, PMA_PMD_LED_OVERR_REG);
|
||||
|
||||
if (bad_lp)
|
||||
pd->bad_lp_tries++;
|
||||
else
|
||||
pd->bad_lp_tries = 0;
|
||||
|
||||
if (pd->bad_lp_tries == MAX_BAD_LP_TRIES) {
|
||||
pd->bad_lp_tries = 0; /* Restart count */
|
||||
reg &= ~(PMA_PMD_LED_FLASH << PMA_PMD_LED_RX_LBN);
|
||||
reg |= (PMA_PMD_LED_FLASH << PMA_PMD_LED_RX_LBN);
|
||||
EFX_ERR(efx, "This NIC appears to be plugged into"
|
||||
" a port that is not 10GBASE-T capable.\n"
|
||||
" This PHY is 10GBASE-T ONLY, so no link can"
|
||||
" be established.\n");
|
||||
} else {
|
||||
reg |= (PMA_PMD_LED_OFF << PMA_PMD_LED_RX_LBN);
|
||||
}
|
||||
mdio_clause45_write(efx, efx->mii.phy_id, MDIO_MMD_PMAPMD,
|
||||
PMA_PMD_LED_OVERR_REG, reg);
|
||||
}
|
||||
|
||||
/* Check link status and return a boolean OK value. If the link is NOT
|
||||
* OK we have a quick rummage round to see if we appear to be plugged
|
||||
* into a non-10GBT port and if so warn the user that they won't get
|
||||
* link any time soon as we are 10GBT only, unless caller specified
|
||||
* not to do this check (it isn't useful in loopback) */
|
||||
static int tenxpress_link_ok(struct efx_nic *efx, int check_lp)
|
||||
{
|
||||
int ok = mdio_clause45_links_ok(efx, TENXPRESS_REQUIRED_DEVS);
|
||||
|
||||
if (ok) {
|
||||
tenxpress_set_bad_lp(efx, 0);
|
||||
} else if (check_lp) {
|
||||
/* Are we plugged into the wrong sort of link? */
|
||||
int bad_lp = 0;
|
||||
int phy_id = efx->mii.phy_id;
|
||||
int an_stat = mdio_clause45_read(efx, phy_id, MDIO_MMD_AN,
|
||||
MDIO_AN_STATUS);
|
||||
int xphy_stat = mdio_clause45_read(efx, phy_id,
|
||||
MDIO_MMD_PMAPMD,
|
||||
PMA_PMD_XSTATUS_REG);
|
||||
/* Are we plugged into anything that sends FLPs? If
|
||||
* not we can't distinguish between not being plugged
|
||||
* in and being plugged into a non-AN antique. The FLP
|
||||
* bit has the advantage of not clearing when autoneg
|
||||
* restarts. */
|
||||
if (!(xphy_stat & (1 << PMA_PMD_XSTAT_FLP_LBN))) {
|
||||
tenxpress_set_bad_lp(efx, 0);
|
||||
return ok;
|
||||
}
|
||||
|
||||
/* If it can do 10GBT it must be XNP capable */
|
||||
bad_lp = !(an_stat & (1 << MDIO_AN_STATUS_XNP_LBN));
|
||||
if (!bad_lp && (an_stat & (1 << MDIO_AN_STATUS_PAGE_LBN))) {
|
||||
bad_lp = !(mdio_clause45_read(efx, phy_id,
|
||||
MDIO_MMD_AN, MDIO_AN_10GBT_STATUS) &
|
||||
(1 << MDIO_AN_10GBT_STATUS_LP_10G_LBN));
|
||||
}
|
||||
tenxpress_set_bad_lp(efx, bad_lp);
|
||||
}
|
||||
return ok;
|
||||
}
|
||||
|
||||
static void tenxpress_phy_reconfigure(struct efx_nic *efx)
|
||||
{
|
||||
if (!tenxpress_state_is(efx, TENXPRESS_STATUS_NORMAL))
|
||||
return;
|
||||
|
||||
efx->link_up = tenxpress_link_ok(efx, 0);
|
||||
efx->link_options = GM_LPA_10000FULL;
|
||||
}
|
||||
|
||||
static void tenxpress_phy_clear_interrupt(struct efx_nic *efx)
|
||||
{
|
||||
/* Nothing done here - LASI interrupts aren't reliable so poll */
|
||||
}
|
||||
|
||||
|
||||
/* Poll PHY for interrupt */
|
||||
static int tenxpress_phy_check_hw(struct efx_nic *efx)
|
||||
{
|
||||
struct tenxpress_phy_data *phy_data = efx->phy_data;
|
||||
int phy_up = tenxpress_state_is(efx, TENXPRESS_STATUS_NORMAL);
|
||||
int link_ok;
|
||||
|
||||
link_ok = phy_up && tenxpress_link_ok(efx, 1);
|
||||
|
||||
if (link_ok != efx->link_up)
|
||||
falcon_xmac_sim_phy_event(efx);
|
||||
|
||||
/* Nothing to check if we've already shut down the PHY */
|
||||
if (!phy_up)
|
||||
return 0;
|
||||
|
||||
if (atomic_read(&phy_data->bad_crc_count) > crc_error_reset_threshold) {
|
||||
EFX_ERR(efx, "Resetting XAUI due to too many CRC errors\n");
|
||||
falcon_reset_xaui(efx);
|
||||
atomic_set(&phy_data->bad_crc_count, 0);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void tenxpress_phy_fini(struct efx_nic *efx)
|
||||
{
|
||||
int reg;
|
||||
|
||||
/* Power down the LNPGA */
|
||||
reg = (1 << PMA_PMD_LNPGA_POWERDOWN_LBN);
|
||||
mdio_clause45_write(efx, efx->mii.phy_id, MDIO_MMD_PMAPMD,
|
||||
PMA_PMD_XCONTROL_REG, reg);
|
||||
|
||||
/* Waiting here ensures that the board fini, which can turn off the
|
||||
* power to the PHY, won't get run until the LNPGA powerdown has been
|
||||
* given long enough to complete. */
|
||||
schedule_timeout_uninterruptible(LNPGA_PDOWN_WAIT); /* 200 ms */
|
||||
|
||||
kfree(efx->phy_data);
|
||||
efx->phy_data = NULL;
|
||||
}
|
||||
|
||||
|
||||
/* Set the RX and TX LEDs and Link LED flashing. The other LEDs
|
||||
* (which probably aren't wired anyway) are left in AUTO mode */
|
||||
void tenxpress_phy_blink(struct efx_nic *efx, int blink)
|
||||
{
|
||||
int reg;
|
||||
|
||||
if (blink)
|
||||
reg = (PMA_PMD_LED_FLASH << PMA_PMD_LED_TX_LBN) |
|
||||
(PMA_PMD_LED_FLASH << PMA_PMD_LED_RX_LBN) |
|
||||
(PMA_PMD_LED_FLASH << PMA_PMD_LED_LINK_LBN);
|
||||
else
|
||||
reg = PMA_PMD_LED_DEFAULT;
|
||||
|
||||
mdio_clause45_write(efx, efx->mii.phy_id, MDIO_MMD_PMAPMD,
|
||||
PMA_PMD_LED_OVERR_REG, reg);
|
||||
}
|
||||
|
||||
static void tenxpress_reset_xaui(struct efx_nic *efx)
|
||||
{
|
||||
int phy = efx->mii.phy_id;
|
||||
int clk_ctrl, test_select, soft_rst2;
|
||||
|
||||
/* Real work is done on clock_ctrl other resets are thought to be
|
||||
* optional but make the reset more reliable
|
||||
*/
|
||||
|
||||
/* Read */
|
||||
clk_ctrl = mdio_clause45_read(efx, phy, MDIO_MMD_PCS,
|
||||
PCS_CLOCK_CTRL_REG);
|
||||
test_select = mdio_clause45_read(efx, phy, MDIO_MMD_PCS,
|
||||
PCS_TEST_SELECT_REG);
|
||||
soft_rst2 = mdio_clause45_read(efx, phy, MDIO_MMD_PCS,
|
||||
PCS_SOFT_RST2_REG);
|
||||
|
||||
/* Put in reset */
|
||||
test_select &= ~(1 << CLK312_EN_LBN);
|
||||
mdio_clause45_write(efx, phy, MDIO_MMD_PCS,
|
||||
PCS_TEST_SELECT_REG, test_select);
|
||||
|
||||
soft_rst2 &= ~((1 << XGXS_RST_N_LBN) | (1 << SERDES_RST_N_LBN));
|
||||
mdio_clause45_write(efx, phy, MDIO_MMD_PCS,
|
||||
PCS_SOFT_RST2_REG, soft_rst2);
|
||||
|
||||
clk_ctrl &= ~(1 << PLL312_RST_N_LBN);
|
||||
mdio_clause45_write(efx, phy, MDIO_MMD_PCS,
|
||||
PCS_CLOCK_CTRL_REG, clk_ctrl);
|
||||
udelay(10);
|
||||
|
||||
/* Remove reset */
|
||||
clk_ctrl |= (1 << PLL312_RST_N_LBN);
|
||||
mdio_clause45_write(efx, phy, MDIO_MMD_PCS,
|
||||
PCS_CLOCK_CTRL_REG, clk_ctrl);
|
||||
udelay(10);
|
||||
|
||||
soft_rst2 |= ((1 << XGXS_RST_N_LBN) | (1 << SERDES_RST_N_LBN));
|
||||
mdio_clause45_write(efx, phy, MDIO_MMD_PCS,
|
||||
PCS_SOFT_RST2_REG, soft_rst2);
|
||||
udelay(10);
|
||||
|
||||
test_select |= (1 << CLK312_EN_LBN);
|
||||
mdio_clause45_write(efx, phy, MDIO_MMD_PCS,
|
||||
PCS_TEST_SELECT_REG, test_select);
|
||||
udelay(10);
|
||||
}
|
||||
|
||||
struct efx_phy_operations falcon_tenxpress_phy_ops = {
|
||||
.init = tenxpress_phy_init,
|
||||
.reconfigure = tenxpress_phy_reconfigure,
|
||||
.check_hw = tenxpress_phy_check_hw,
|
||||
.fini = tenxpress_phy_fini,
|
||||
.clear_interrupt = tenxpress_phy_clear_interrupt,
|
||||
.reset_xaui = tenxpress_reset_xaui,
|
||||
.mmds = TENXPRESS_REQUIRED_DEVS,
|
||||
};
|
452
drivers/net/sfc/tx.c
Normal file
452
drivers/net/sfc/tx.c
Normal file
@ -0,0 +1,452 @@
|
||||
/****************************************************************************
|
||||
* Driver for Solarflare Solarstorm network controllers and boards
|
||||
* Copyright 2005-2006 Fen Systems Ltd.
|
||||
* Copyright 2005-2008 Solarflare Communications Inc.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 as published
|
||||
* by the Free Software Foundation, incorporated herein by reference.
|
||||
*/
|
||||
|
||||
#include <linux/pci.h>
|
||||
#include <linux/tcp.h>
|
||||
#include <linux/ip.h>
|
||||
#include <linux/in.h>
|
||||
#include <linux/if_ether.h>
|
||||
#include <linux/highmem.h>
|
||||
#include "net_driver.h"
|
||||
#include "tx.h"
|
||||
#include "efx.h"
|
||||
#include "falcon.h"
|
||||
#include "workarounds.h"
|
||||
|
||||
/*
|
||||
* TX descriptor ring full threshold
|
||||
*
|
||||
* The tx_queue descriptor ring fill-level must fall below this value
|
||||
* before we restart the netif queue
|
||||
*/
|
||||
#define EFX_NETDEV_TX_THRESHOLD(_tx_queue) \
|
||||
(_tx_queue->efx->type->txd_ring_mask / 2u)
|
||||
|
||||
/* We want to be able to nest calls to netif_stop_queue(), since each
|
||||
* channel can have an individual stop on the queue.
|
||||
*/
|
||||
void efx_stop_queue(struct efx_nic *efx)
|
||||
{
|
||||
spin_lock_bh(&efx->netif_stop_lock);
|
||||
EFX_TRACE(efx, "stop TX queue\n");
|
||||
|
||||
atomic_inc(&efx->netif_stop_count);
|
||||
netif_stop_queue(efx->net_dev);
|
||||
|
||||
spin_unlock_bh(&efx->netif_stop_lock);
|
||||
}
|
||||
|
||||
/* Wake netif's TX queue
|
||||
* We want to be able to nest calls to netif_stop_queue(), since each
|
||||
* channel can have an individual stop on the queue.
|
||||
*/
|
||||
inline void efx_wake_queue(struct efx_nic *efx)
|
||||
{
|
||||
local_bh_disable();
|
||||
if (atomic_dec_and_lock(&efx->netif_stop_count,
|
||||
&efx->netif_stop_lock)) {
|
||||
EFX_TRACE(efx, "waking TX queue\n");
|
||||
netif_wake_queue(efx->net_dev);
|
||||
spin_unlock(&efx->netif_stop_lock);
|
||||
}
|
||||
local_bh_enable();
|
||||
}
|
||||
|
||||
static inline void efx_dequeue_buffer(struct efx_tx_queue *tx_queue,
|
||||
struct efx_tx_buffer *buffer)
|
||||
{
|
||||
if (buffer->unmap_len) {
|
||||
struct pci_dev *pci_dev = tx_queue->efx->pci_dev;
|
||||
if (buffer->unmap_single)
|
||||
pci_unmap_single(pci_dev, buffer->unmap_addr,
|
||||
buffer->unmap_len, PCI_DMA_TODEVICE);
|
||||
else
|
||||
pci_unmap_page(pci_dev, buffer->unmap_addr,
|
||||
buffer->unmap_len, PCI_DMA_TODEVICE);
|
||||
buffer->unmap_len = 0;
|
||||
buffer->unmap_single = 0;
|
||||
}
|
||||
|
||||
if (buffer->skb) {
|
||||
dev_kfree_skb_any((struct sk_buff *) buffer->skb);
|
||||
buffer->skb = NULL;
|
||||
EFX_TRACE(tx_queue->efx, "TX queue %d transmission id %x "
|
||||
"complete\n", tx_queue->queue, read_ptr);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Add a socket buffer to a TX queue
|
||||
*
|
||||
* This maps all fragments of a socket buffer for DMA and adds them to
|
||||
* the TX queue. The queue's insert pointer will be incremented by
|
||||
* the number of fragments in the socket buffer.
|
||||
*
|
||||
* If any DMA mapping fails, any mapped fragments will be unmapped,
|
||||
* the queue's insert pointer will be restored to its original value.
|
||||
*
|
||||
* Returns NETDEV_TX_OK or NETDEV_TX_BUSY
|
||||
* You must hold netif_tx_lock() to call this function.
|
||||
*/
|
||||
static inline int efx_enqueue_skb(struct efx_tx_queue *tx_queue,
|
||||
const struct sk_buff *skb)
|
||||
{
|
||||
struct efx_nic *efx = tx_queue->efx;
|
||||
struct pci_dev *pci_dev = efx->pci_dev;
|
||||
struct efx_tx_buffer *buffer;
|
||||
skb_frag_t *fragment;
|
||||
struct page *page;
|
||||
int page_offset;
|
||||
unsigned int len, unmap_len = 0, fill_level, insert_ptr, misalign;
|
||||
dma_addr_t dma_addr, unmap_addr = 0;
|
||||
unsigned int dma_len;
|
||||
unsigned unmap_single;
|
||||
int q_space, i = 0;
|
||||
int rc = NETDEV_TX_OK;
|
||||
|
||||
EFX_BUG_ON_PARANOID(tx_queue->write_count != tx_queue->insert_count);
|
||||
|
||||
/* Get size of the initial fragment */
|
||||
len = skb_headlen(skb);
|
||||
|
||||
fill_level = tx_queue->insert_count - tx_queue->old_read_count;
|
||||
q_space = efx->type->txd_ring_mask - 1 - fill_level;
|
||||
|
||||
/* Map for DMA. Use pci_map_single rather than pci_map_page
|
||||
* since this is more efficient on machines with sparse
|
||||
* memory.
|
||||
*/
|
||||
unmap_single = 1;
|
||||
dma_addr = pci_map_single(pci_dev, skb->data, len, PCI_DMA_TODEVICE);
|
||||
|
||||
/* Process all fragments */
|
||||
while (1) {
|
||||
if (unlikely(pci_dma_mapping_error(dma_addr)))
|
||||
goto pci_err;
|
||||
|
||||
/* Store fields for marking in the per-fragment final
|
||||
* descriptor */
|
||||
unmap_len = len;
|
||||
unmap_addr = dma_addr;
|
||||
|
||||
/* Add to TX queue, splitting across DMA boundaries */
|
||||
do {
|
||||
if (unlikely(q_space-- <= 0)) {
|
||||
/* It might be that completions have
|
||||
* happened since the xmit path last
|
||||
* checked. Update the xmit path's
|
||||
* copy of read_count.
|
||||
*/
|
||||
++tx_queue->stopped;
|
||||
/* This memory barrier protects the
|
||||
* change of stopped from the access
|
||||
* of read_count. */
|
||||
smp_mb();
|
||||
tx_queue->old_read_count =
|
||||
*(volatile unsigned *)
|
||||
&tx_queue->read_count;
|
||||
fill_level = (tx_queue->insert_count
|
||||
- tx_queue->old_read_count);
|
||||
q_space = (efx->type->txd_ring_mask - 1 -
|
||||
fill_level);
|
||||
if (unlikely(q_space-- <= 0))
|
||||
goto stop;
|
||||
smp_mb();
|
||||
--tx_queue->stopped;
|
||||
}
|
||||
|
||||
insert_ptr = (tx_queue->insert_count &
|
||||
efx->type->txd_ring_mask);
|
||||
buffer = &tx_queue->buffer[insert_ptr];
|
||||
EFX_BUG_ON_PARANOID(buffer->skb);
|
||||
EFX_BUG_ON_PARANOID(buffer->len);
|
||||
EFX_BUG_ON_PARANOID(buffer->continuation != 1);
|
||||
EFX_BUG_ON_PARANOID(buffer->unmap_len);
|
||||
|
||||
dma_len = (((~dma_addr) & efx->type->tx_dma_mask) + 1);
|
||||
if (likely(dma_len > len))
|
||||
dma_len = len;
|
||||
|
||||
misalign = (unsigned)dma_addr & efx->type->bug5391_mask;
|
||||
if (misalign && dma_len + misalign > 512)
|
||||
dma_len = 512 - misalign;
|
||||
|
||||
/* Fill out per descriptor fields */
|
||||
buffer->len = dma_len;
|
||||
buffer->dma_addr = dma_addr;
|
||||
len -= dma_len;
|
||||
dma_addr += dma_len;
|
||||
++tx_queue->insert_count;
|
||||
} while (len);
|
||||
|
||||
/* Transfer ownership of the unmapping to the final buffer */
|
||||
buffer->unmap_addr = unmap_addr;
|
||||
buffer->unmap_single = unmap_single;
|
||||
buffer->unmap_len = unmap_len;
|
||||
unmap_len = 0;
|
||||
|
||||
/* Get address and size of next fragment */
|
||||
if (i >= skb_shinfo(skb)->nr_frags)
|
||||
break;
|
||||
fragment = &skb_shinfo(skb)->frags[i];
|
||||
len = fragment->size;
|
||||
page = fragment->page;
|
||||
page_offset = fragment->page_offset;
|
||||
i++;
|
||||
/* Map for DMA */
|
||||
unmap_single = 0;
|
||||
dma_addr = pci_map_page(pci_dev, page, page_offset, len,
|
||||
PCI_DMA_TODEVICE);
|
||||
}
|
||||
|
||||
/* Transfer ownership of the skb to the final buffer */
|
||||
buffer->skb = skb;
|
||||
buffer->continuation = 0;
|
||||
|
||||
/* Pass off to hardware */
|
||||
falcon_push_buffers(tx_queue);
|
||||
|
||||
return NETDEV_TX_OK;
|
||||
|
||||
pci_err:
|
||||
EFX_ERR_RL(efx, " TX queue %d could not map skb with %d bytes %d "
|
||||
"fragments for DMA\n", tx_queue->queue, skb->len,
|
||||
skb_shinfo(skb)->nr_frags + 1);
|
||||
|
||||
/* Mark the packet as transmitted, and free the SKB ourselves */
|
||||
dev_kfree_skb_any((struct sk_buff *)skb);
|
||||
goto unwind;
|
||||
|
||||
stop:
|
||||
rc = NETDEV_TX_BUSY;
|
||||
|
||||
if (tx_queue->stopped == 1)
|
||||
efx_stop_queue(efx);
|
||||
|
||||
unwind:
|
||||
/* Work backwards until we hit the original insert pointer value */
|
||||
while (tx_queue->insert_count != tx_queue->write_count) {
|
||||
--tx_queue->insert_count;
|
||||
insert_ptr = tx_queue->insert_count & efx->type->txd_ring_mask;
|
||||
buffer = &tx_queue->buffer[insert_ptr];
|
||||
efx_dequeue_buffer(tx_queue, buffer);
|
||||
buffer->len = 0;
|
||||
}
|
||||
|
||||
/* Free the fragment we were mid-way through pushing */
|
||||
if (unmap_len)
|
||||
pci_unmap_page(pci_dev, unmap_addr, unmap_len,
|
||||
PCI_DMA_TODEVICE);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* Remove packets from the TX queue
|
||||
*
|
||||
* This removes packets from the TX queue, up to and including the
|
||||
* specified index.
|
||||
*/
|
||||
static inline void efx_dequeue_buffers(struct efx_tx_queue *tx_queue,
|
||||
unsigned int index)
|
||||
{
|
||||
struct efx_nic *efx = tx_queue->efx;
|
||||
unsigned int stop_index, read_ptr;
|
||||
unsigned int mask = tx_queue->efx->type->txd_ring_mask;
|
||||
|
||||
stop_index = (index + 1) & mask;
|
||||
read_ptr = tx_queue->read_count & mask;
|
||||
|
||||
while (read_ptr != stop_index) {
|
||||
struct efx_tx_buffer *buffer = &tx_queue->buffer[read_ptr];
|
||||
if (unlikely(buffer->len == 0)) {
|
||||
EFX_ERR(tx_queue->efx, "TX queue %d spurious TX "
|
||||
"completion id %x\n", tx_queue->queue,
|
||||
read_ptr);
|
||||
efx_schedule_reset(efx, RESET_TYPE_TX_SKIP);
|
||||
return;
|
||||
}
|
||||
|
||||
efx_dequeue_buffer(tx_queue, buffer);
|
||||
buffer->continuation = 1;
|
||||
buffer->len = 0;
|
||||
|
||||
++tx_queue->read_count;
|
||||
read_ptr = tx_queue->read_count & mask;
|
||||
}
|
||||
}
|
||||
|
||||
/* Initiate a packet transmission on the specified TX queue.
|
||||
* Note that returning anything other than NETDEV_TX_OK will cause the
|
||||
* OS to free the skb.
|
||||
*
|
||||
* This function is split out from efx_hard_start_xmit to allow the
|
||||
* loopback test to direct packets via specific TX queues. It is
|
||||
* therefore a non-static inline, so as not to penalise performance
|
||||
* for non-loopback transmissions.
|
||||
*
|
||||
* Context: netif_tx_lock held
|
||||
*/
|
||||
inline int efx_xmit(struct efx_nic *efx,
|
||||
struct efx_tx_queue *tx_queue, struct sk_buff *skb)
|
||||
{
|
||||
int rc;
|
||||
|
||||
/* Map fragments for DMA and add to TX queue */
|
||||
rc = efx_enqueue_skb(tx_queue, skb);
|
||||
if (unlikely(rc != NETDEV_TX_OK))
|
||||
goto out;
|
||||
|
||||
/* Update last TX timer */
|
||||
efx->net_dev->trans_start = jiffies;
|
||||
|
||||
out:
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* Initiate a packet transmission. We use one channel per CPU
|
||||
* (sharing when we have more CPUs than channels). On Falcon, the TX
|
||||
* completion events will be directed back to the CPU that transmitted
|
||||
* the packet, which should be cache-efficient.
|
||||
*
|
||||
* Context: non-blocking.
|
||||
* Note that returning anything other than NETDEV_TX_OK will cause the
|
||||
* OS to free the skb.
|
||||
*/
|
||||
int efx_hard_start_xmit(struct sk_buff *skb, struct net_device *net_dev)
|
||||
{
|
||||
struct efx_nic *efx = net_dev->priv;
|
||||
return efx_xmit(efx, &efx->tx_queue[0], skb);
|
||||
}
|
||||
|
||||
void efx_xmit_done(struct efx_tx_queue *tx_queue, unsigned int index)
|
||||
{
|
||||
unsigned fill_level;
|
||||
struct efx_nic *efx = tx_queue->efx;
|
||||
|
||||
EFX_BUG_ON_PARANOID(index > efx->type->txd_ring_mask);
|
||||
|
||||
efx_dequeue_buffers(tx_queue, index);
|
||||
|
||||
/* See if we need to restart the netif queue. This barrier
|
||||
* separates the update of read_count from the test of
|
||||
* stopped. */
|
||||
smp_mb();
|
||||
if (unlikely(tx_queue->stopped)) {
|
||||
fill_level = tx_queue->insert_count - tx_queue->read_count;
|
||||
if (fill_level < EFX_NETDEV_TX_THRESHOLD(tx_queue)) {
|
||||
EFX_BUG_ON_PARANOID(!NET_DEV_REGISTERED(efx));
|
||||
|
||||
/* Do this under netif_tx_lock(), to avoid racing
|
||||
* with efx_xmit(). */
|
||||
netif_tx_lock(efx->net_dev);
|
||||
if (tx_queue->stopped) {
|
||||
tx_queue->stopped = 0;
|
||||
efx_wake_queue(efx);
|
||||
}
|
||||
netif_tx_unlock(efx->net_dev);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int efx_probe_tx_queue(struct efx_tx_queue *tx_queue)
|
||||
{
|
||||
struct efx_nic *efx = tx_queue->efx;
|
||||
unsigned int txq_size;
|
||||
int i, rc;
|
||||
|
||||
EFX_LOG(efx, "creating TX queue %d\n", tx_queue->queue);
|
||||
|
||||
/* Allocate software ring */
|
||||
txq_size = (efx->type->txd_ring_mask + 1) * sizeof(*tx_queue->buffer);
|
||||
tx_queue->buffer = kzalloc(txq_size, GFP_KERNEL);
|
||||
if (!tx_queue->buffer) {
|
||||
rc = -ENOMEM;
|
||||
goto fail1;
|
||||
}
|
||||
for (i = 0; i <= efx->type->txd_ring_mask; ++i)
|
||||
tx_queue->buffer[i].continuation = 1;
|
||||
|
||||
/* Allocate hardware ring */
|
||||
rc = falcon_probe_tx(tx_queue);
|
||||
if (rc)
|
||||
goto fail2;
|
||||
|
||||
return 0;
|
||||
|
||||
fail2:
|
||||
kfree(tx_queue->buffer);
|
||||
tx_queue->buffer = NULL;
|
||||
fail1:
|
||||
tx_queue->used = 0;
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
int efx_init_tx_queue(struct efx_tx_queue *tx_queue)
|
||||
{
|
||||
EFX_LOG(tx_queue->efx, "initialising TX queue %d\n", tx_queue->queue);
|
||||
|
||||
tx_queue->insert_count = 0;
|
||||
tx_queue->write_count = 0;
|
||||
tx_queue->read_count = 0;
|
||||
tx_queue->old_read_count = 0;
|
||||
BUG_ON(tx_queue->stopped);
|
||||
|
||||
/* Set up TX descriptor ring */
|
||||
return falcon_init_tx(tx_queue);
|
||||
}
|
||||
|
||||
void efx_release_tx_buffers(struct efx_tx_queue *tx_queue)
|
||||
{
|
||||
struct efx_tx_buffer *buffer;
|
||||
|
||||
if (!tx_queue->buffer)
|
||||
return;
|
||||
|
||||
/* Free any buffers left in the ring */
|
||||
while (tx_queue->read_count != tx_queue->write_count) {
|
||||
buffer = &tx_queue->buffer[tx_queue->read_count &
|
||||
tx_queue->efx->type->txd_ring_mask];
|
||||
efx_dequeue_buffer(tx_queue, buffer);
|
||||
buffer->continuation = 1;
|
||||
buffer->len = 0;
|
||||
|
||||
++tx_queue->read_count;
|
||||
}
|
||||
}
|
||||
|
||||
void efx_fini_tx_queue(struct efx_tx_queue *tx_queue)
|
||||
{
|
||||
EFX_LOG(tx_queue->efx, "shutting down TX queue %d\n", tx_queue->queue);
|
||||
|
||||
/* Flush TX queue, remove descriptor ring */
|
||||
falcon_fini_tx(tx_queue);
|
||||
|
||||
efx_release_tx_buffers(tx_queue);
|
||||
|
||||
/* Release queue's stop on port, if any */
|
||||
if (tx_queue->stopped) {
|
||||
tx_queue->stopped = 0;
|
||||
efx_wake_queue(tx_queue->efx);
|
||||
}
|
||||
}
|
||||
|
||||
void efx_remove_tx_queue(struct efx_tx_queue *tx_queue)
|
||||
{
|
||||
EFX_LOG(tx_queue->efx, "destroying TX queue %d\n", tx_queue->queue);
|
||||
falcon_remove_tx(tx_queue);
|
||||
|
||||
kfree(tx_queue->buffer);
|
||||
tx_queue->buffer = NULL;
|
||||
tx_queue->used = 0;
|
||||
}
|
||||
|
||||
|
24
drivers/net/sfc/tx.h
Normal file
24
drivers/net/sfc/tx.h
Normal file
@ -0,0 +1,24 @@
|
||||
/****************************************************************************
|
||||
* Driver for Solarflare Solarstorm network controllers and boards
|
||||
* Copyright 2006 Fen Systems Ltd.
|
||||
* Copyright 2006-2008 Solarflare Communications Inc.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 as published
|
||||
* by the Free Software Foundation, incorporated herein by reference.
|
||||
*/
|
||||
|
||||
#ifndef EFX_TX_H
|
||||
#define EFX_TX_H
|
||||
|
||||
#include "net_driver.h"
|
||||
|
||||
int efx_probe_tx_queue(struct efx_tx_queue *tx_queue);
|
||||
void efx_remove_tx_queue(struct efx_tx_queue *tx_queue);
|
||||
int efx_init_tx_queue(struct efx_tx_queue *tx_queue);
|
||||
void efx_fini_tx_queue(struct efx_tx_queue *tx_queue);
|
||||
|
||||
int efx_hard_start_xmit(struct sk_buff *skb, struct net_device *net_dev);
|
||||
void efx_release_tx_buffers(struct efx_tx_queue *tx_queue);
|
||||
|
||||
#endif /* EFX_TX_H */
|
56
drivers/net/sfc/workarounds.h
Normal file
56
drivers/net/sfc/workarounds.h
Normal file
@ -0,0 +1,56 @@
|
||||
/****************************************************************************
|
||||
* Driver for Solarflare Solarstorm network controllers and boards
|
||||
* Copyright 2006-2008 Solarflare Communications Inc.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 as published
|
||||
* by the Free Software Foundation, incorporated herein by reference.
|
||||
*/
|
||||
|
||||
#ifndef EFX_WORKAROUNDS_H
|
||||
#define EFX_WORKAROUNDS_H
|
||||
|
||||
/*
|
||||
* Hardware workarounds.
|
||||
* Bug numbers are from Solarflare's Bugzilla.
|
||||
*/
|
||||
|
||||
#define EFX_WORKAROUND_ALWAYS(efx) 1
|
||||
#define EFX_WORKAROUND_FALCON_A(efx) (FALCON_REV(efx) <= FALCON_REV_A1)
|
||||
|
||||
/* XAUI resets if link not detected */
|
||||
#define EFX_WORKAROUND_5147 EFX_WORKAROUND_ALWAYS
|
||||
/* SNAP frames have TOBE_DISC set */
|
||||
#define EFX_WORKAROUND_5475 EFX_WORKAROUND_ALWAYS
|
||||
/* RX PCIe double split performance issue */
|
||||
#define EFX_WORKAROUND_7575 EFX_WORKAROUND_ALWAYS
|
||||
/* TX pkt parser problem with <= 16 byte TXes */
|
||||
#define EFX_WORKAROUND_9141 EFX_WORKAROUND_ALWAYS
|
||||
/* XGXS and XAUI reset sequencing in SW */
|
||||
#define EFX_WORKAROUND_9388 EFX_WORKAROUND_ALWAYS
|
||||
/* Low rate CRC errors require XAUI reset */
|
||||
#define EFX_WORKAROUND_10750 EFX_WORKAROUND_ALWAYS
|
||||
/* TX_EV_PKT_ERR can be caused by a dangling TX descriptor
|
||||
* or a PCIe error (bug 11028) */
|
||||
#define EFX_WORKAROUND_10727 EFX_WORKAROUND_ALWAYS
|
||||
/* Transmit flow control may get disabled */
|
||||
#define EFX_WORKAROUND_11482 EFX_WORKAROUND_ALWAYS
|
||||
/* Flush events can take a very long time to appear */
|
||||
#define EFX_WORKAROUND_11557 EFX_WORKAROUND_ALWAYS
|
||||
|
||||
/* Spurious parity errors in TSORT buffers */
|
||||
#define EFX_WORKAROUND_5129 EFX_WORKAROUND_FALCON_A
|
||||
/* iSCSI parsing errors */
|
||||
#define EFX_WORKAROUND_5583 EFX_WORKAROUND_FALCON_A
|
||||
/* RX events go missing */
|
||||
#define EFX_WORKAROUND_5676 EFX_WORKAROUND_FALCON_A
|
||||
/* RX_RESET on A1 */
|
||||
#define EFX_WORKAROUND_6555 EFX_WORKAROUND_FALCON_A
|
||||
/* Increase filter depth to avoid RX_RESET */
|
||||
#define EFX_WORKAROUND_7244 EFX_WORKAROUND_FALCON_A
|
||||
/* Flushes may never complete */
|
||||
#define EFX_WORKAROUND_7803 EFX_WORKAROUND_FALCON_A
|
||||
/* Leak overlength packets rather than free */
|
||||
#define EFX_WORKAROUND_8071 EFX_WORKAROUND_FALCON_A
|
||||
|
||||
#endif /* EFX_WORKAROUNDS_H */
|
62
drivers/net/sfc/xenpack.h
Normal file
62
drivers/net/sfc/xenpack.h
Normal file
@ -0,0 +1,62 @@
|
||||
/****************************************************************************
|
||||
* Driver for Solarflare Solarstorm network controllers and boards
|
||||
* Copyright 2006 Solarflare Communications Inc.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 as published
|
||||
* by the Free Software Foundation, incorporated herein by reference.
|
||||
*/
|
||||
|
||||
#ifndef EFX_XENPACK_H
|
||||
#define EFX_XENPACK_H
|
||||
|
||||
/* Exported functions from Xenpack standard PHY control */
|
||||
|
||||
#include "mdio_10g.h"
|
||||
|
||||
/****************************************************************************/
|
||||
/* XENPACK MDIO register extensions */
|
||||
#define MDIO_XP_LASI_RX_CTRL (0x9000)
|
||||
#define MDIO_XP_LASI_TX_CTRL (0x9001)
|
||||
#define MDIO_XP_LASI_CTRL (0x9002)
|
||||
#define MDIO_XP_LASI_RX_STAT (0x9003)
|
||||
#define MDIO_XP_LASI_TX_STAT (0x9004)
|
||||
#define MDIO_XP_LASI_STAT (0x9005)
|
||||
|
||||
/* Control/Status bits */
|
||||
#define XP_LASI_LS_ALARM (1 << 0)
|
||||
#define XP_LASI_TX_ALARM (1 << 1)
|
||||
#define XP_LASI_RX_ALARM (1 << 2)
|
||||
/* These two are Quake vendor extensions to the standard XENPACK defines */
|
||||
#define XP_LASI_LS_INTB (1 << 3)
|
||||
#define XP_LASI_TEST (1 << 7)
|
||||
|
||||
/* Enable LASI interrupts for PHY */
|
||||
static inline void xenpack_enable_lasi_irqs(struct efx_nic *efx)
|
||||
{
|
||||
int reg;
|
||||
int phy_id = efx->mii.phy_id;
|
||||
/* Read to clear LASI status register */
|
||||
reg = mdio_clause45_read(efx, phy_id, MDIO_MMD_PMAPMD,
|
||||
MDIO_XP_LASI_STAT);
|
||||
|
||||
mdio_clause45_write(efx, phy_id, MDIO_MMD_PMAPMD,
|
||||
MDIO_XP_LASI_CTRL, XP_LASI_LS_ALARM);
|
||||
}
|
||||
|
||||
/* Read the LASI interrupt status to clear the interrupt. */
|
||||
static inline int xenpack_clear_lasi_irqs(struct efx_nic *efx)
|
||||
{
|
||||
/* Read to clear link status alarm */
|
||||
return mdio_clause45_read(efx, efx->mii.phy_id,
|
||||
MDIO_MMD_PMAPMD, MDIO_XP_LASI_STAT);
|
||||
}
|
||||
|
||||
/* Turn off LASI interrupts */
|
||||
static inline void xenpack_disable_lasi_irqs(struct efx_nic *efx)
|
||||
{
|
||||
mdio_clause45_write(efx, efx->mii.phy_id, MDIO_MMD_PMAPMD,
|
||||
MDIO_XP_LASI_CTRL, 0);
|
||||
}
|
||||
|
||||
#endif /* EFX_XENPACK_H */
|
132
drivers/net/sfc/xfp_phy.c
Normal file
132
drivers/net/sfc/xfp_phy.c
Normal file
@ -0,0 +1,132 @@
|
||||
/****************************************************************************
|
||||
* Driver for Solarflare Solarstorm network controllers and boards
|
||||
* Copyright 2006-2008 Solarflare Communications Inc.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 as published
|
||||
* by the Free Software Foundation, incorporated herein by reference.
|
||||
*/
|
||||
/*
|
||||
* Driver for XFP optical PHYs (plus some support specific to the Quake 2032)
|
||||
* See www.amcc.com for details (search for qt2032)
|
||||
*/
|
||||
|
||||
#include <linux/timer.h>
|
||||
#include <linux/delay.h>
|
||||
#include "efx.h"
|
||||
#include "gmii.h"
|
||||
#include "mdio_10g.h"
|
||||
#include "xenpack.h"
|
||||
#include "phy.h"
|
||||
#include "mac.h"
|
||||
|
||||
#define XFP_REQUIRED_DEVS (MDIO_MMDREG_DEVS0_PCS | \
|
||||
MDIO_MMDREG_DEVS0_PMAPMD | \
|
||||
MDIO_MMDREG_DEVS0_PHYXS)
|
||||
|
||||
/****************************************************************************/
|
||||
/* Quake-specific MDIO registers */
|
||||
#define MDIO_QUAKE_LED0_REG (0xD006)
|
||||
|
||||
void xfp_set_led(struct efx_nic *p, int led, int mode)
|
||||
{
|
||||
int addr = MDIO_QUAKE_LED0_REG + led;
|
||||
mdio_clause45_write(p, p->mii.phy_id, MDIO_MMD_PMAPMD, addr,
|
||||
mode);
|
||||
}
|
||||
|
||||
#define XFP_MAX_RESET_TIME 500
|
||||
#define XFP_RESET_WAIT 10
|
||||
|
||||
/* Reset the PHYXS MMD. This is documented (for the Quake PHY) as doing
|
||||
* a complete soft reset.
|
||||
*/
|
||||
static int xfp_reset_phy(struct efx_nic *efx)
|
||||
{
|
||||
int rc;
|
||||
|
||||
rc = mdio_clause45_reset_mmd(efx, MDIO_MMD_PHYXS,
|
||||
XFP_MAX_RESET_TIME / XFP_RESET_WAIT,
|
||||
XFP_RESET_WAIT);
|
||||
if (rc < 0)
|
||||
goto fail;
|
||||
|
||||
/* Wait 250ms for the PHY to complete bootup */
|
||||
msleep(250);
|
||||
|
||||
/* Check that all the MMDs we expect are present and responding. We
|
||||
* expect faults on some if the link is down, but not on the PHY XS */
|
||||
rc = mdio_clause45_check_mmds(efx, XFP_REQUIRED_DEVS,
|
||||
MDIO_MMDREG_DEVS0_PHYXS);
|
||||
if (rc < 0)
|
||||
goto fail;
|
||||
|
||||
efx->board_info.init_leds(efx);
|
||||
|
||||
return rc;
|
||||
|
||||
fail:
|
||||
EFX_ERR(efx, "XFP: reset timed out!\n");
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int xfp_phy_init(struct efx_nic *efx)
|
||||
{
|
||||
u32 devid = mdio_clause45_read_id(efx, MDIO_MMD_PHYXS);
|
||||
int rc;
|
||||
|
||||
EFX_INFO(efx, "XFP: PHY ID reg %x (OUI %x model %x revision"
|
||||
" %x)\n", devid, MDIO_ID_OUI(devid), MDIO_ID_MODEL(devid),
|
||||
MDIO_ID_REV(devid));
|
||||
|
||||
rc = xfp_reset_phy(efx);
|
||||
|
||||
EFX_INFO(efx, "XFP: PHY init %s.\n",
|
||||
rc ? "failed" : "successful");
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static void xfp_phy_clear_interrupt(struct efx_nic *efx)
|
||||
{
|
||||
xenpack_clear_lasi_irqs(efx);
|
||||
}
|
||||
|
||||
static int xfp_link_ok(struct efx_nic *efx)
|
||||
{
|
||||
return mdio_clause45_links_ok(efx, XFP_REQUIRED_DEVS);
|
||||
}
|
||||
|
||||
static int xfp_phy_check_hw(struct efx_nic *efx)
|
||||
{
|
||||
int rc = 0;
|
||||
int link_up = xfp_link_ok(efx);
|
||||
/* Simulate a PHY event if link state has changed */
|
||||
if (link_up != efx->link_up)
|
||||
falcon_xmac_sim_phy_event(efx);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static void xfp_phy_reconfigure(struct efx_nic *efx)
|
||||
{
|
||||
efx->link_up = xfp_link_ok(efx);
|
||||
efx->link_options = GM_LPA_10000FULL;
|
||||
}
|
||||
|
||||
|
||||
static void xfp_phy_fini(struct efx_nic *efx)
|
||||
{
|
||||
/* Clobber the LED if it was blinking */
|
||||
efx->board_info.blink(efx, 0);
|
||||
}
|
||||
|
||||
struct efx_phy_operations falcon_xfp_phy_ops = {
|
||||
.init = xfp_phy_init,
|
||||
.reconfigure = xfp_phy_reconfigure,
|
||||
.check_hw = xfp_phy_check_hw,
|
||||
.fini = xfp_phy_fini,
|
||||
.clear_interrupt = xfp_phy_clear_interrupt,
|
||||
.reset_xaui = efx_port_dummy_op_void,
|
||||
.mmds = XFP_REQUIRED_DEVS,
|
||||
};
|
Loading…
Reference in New Issue
Block a user