add idl4k kernel firmware version 1.13.0.105

This commit is contained in:
Jaroslav Kysela
2015-03-26 17:22:37 +01:00
parent 5194d2792e
commit e9070cdc77
31064 changed files with 12769984 additions and 0 deletions

View File

@@ -0,0 +1,21 @@
config SFC
tristate "Solarflare Solarstorm SFC4000 support"
depends on PCI && INET
select MDIO
select CRC32
select I2C
select I2C_ALGOBIT
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.
config SFC_MTD
bool "Solarflare Solarstorm SFC4000 flash MTD support"
depends on SFC && MTD && !(SFC=y && MTD=m)
default y
help
This exposes the on-board flash memory as an MTD device (e.g.
/dev/mtd1). This makes it possible to upload new boot code
to the NIC.

View File

@@ -0,0 +1,6 @@
sfc-y += efx.o falcon.o tx.o rx.o falcon_gmac.o \
falcon_xmac.o selftest.o ethtool.o xfp_phy.o \
mdio_10g.o tenxpress.o boards.o sfe4001.o
sfc-$(CONFIG_SFC_MTD) += mtd.o
obj-$(CONFIG_SFC) += sfc.o

View File

@@ -0,0 +1,549 @@
/****************************************************************************
* 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(width) \
((width) == 64 ? ~((u64) 0) : \
(((((u64) 1) << (width))) - 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(width) \
((width) == 32 ? ~((u32) 0) : \
(((((u32) 1) << (width))) - 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)) & \
EFX_MASK64(high + 1 - low))
#define EFX_EXTRACT_QWORD64(qword, low, high) \
(EFX_EXTRACT64((qword).u64[0], 0, 63, low, high) & \
EFX_MASK64(high + 1 - low))
#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)) & \
EFX_MASK32(high + 1 - low))
#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)) & \
EFX_MASK32(high + 1 - low))
#define EFX_EXTRACT_DWORD(dword, low, high) \
(EFX_EXTRACT32((dword).u32[0], 0, 31, low, high) & \
EFX_MASK32(high + 1 - low))
#define EFX_OWORD_FIELD64(oword, field) \
EFX_EXTRACT_OWORD64(oword, EFX_LOW_BIT(field), \
EFX_HIGH_BIT(field))
#define EFX_QWORD_FIELD64(qword, field) \
EFX_EXTRACT_QWORD64(qword, EFX_LOW_BIT(field), \
EFX_HIGH_BIT(field))
#define EFX_OWORD_FIELD32(oword, field) \
EFX_EXTRACT_OWORD32(oword, EFX_LOW_BIT(field), \
EFX_HIGH_BIT(field))
#define EFX_QWORD_FIELD32(qword, field) \
EFX_EXTRACT_QWORD32(qword, EFX_LOW_BIT(field), \
EFX_HIGH_BIT(field))
#define EFX_DWORD_FIELD(dword, field) \
EFX_EXTRACT_DWORD(dword, EFX_LOW_BIT(field), \
EFX_HIGH_BIT(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_AND_OWORD(oword, from, mask) \
do { \
(oword).u64[0] = (from).u64[0] & (mask).u64[0]; \
(oword).u64[1] = (from).u64[1] & (mask).u64[1]; \
} while (0)
#define EFX_OR_OWORD(oword, from, mask) \
do { \
(oword).u64[0] = (from).u64[0] | (mask).u64[0]; \
(oword).u64[1] = (from).u64[1] | (mask).u64[1]; \
} while (0)
#define EFX_INSERT64(min, max, low, high, value) \
cpu_to_le64(EFX_INSERT_NATIVE(min, max, low, high, value))
#define EFX_INSERT32(min, max, low, high, value) \
cpu_to_le32(EFX_INSERT_NATIVE(min, max, low, high, value))
#define EFX_INPLACE_MASK64(min, max, low, high) \
EFX_INSERT64(min, max, low, high, EFX_MASK64(high + 1 - low))
#define EFX_INPLACE_MASK32(min, max, low, high) \
EFX_INSERT32(min, max, low, high, EFX_MASK32(high + 1 - low))
#define EFX_SET_OWORD64(oword, low, high, value) do { \
(oword).u64[0] = (((oword).u64[0] \
& ~EFX_INPLACE_MASK64(0, 63, low, high)) \
| EFX_INSERT64(0, 63, low, high, value)); \
(oword).u64[1] = (((oword).u64[1] \
& ~EFX_INPLACE_MASK64(64, 127, low, high)) \
| EFX_INSERT64(64, 127, low, high, value)); \
} while (0)
#define EFX_SET_QWORD64(qword, low, high, value) do { \
(qword).u64[0] = (((qword).u64[0] \
& ~EFX_INPLACE_MASK64(0, 63, low, high)) \
| EFX_INSERT64(0, 63, low, high, value)); \
} while (0)
#define EFX_SET_OWORD32(oword, low, high, value) do { \
(oword).u32[0] = (((oword).u32[0] \
& ~EFX_INPLACE_MASK32(0, 31, low, high)) \
| EFX_INSERT32(0, 31, low, high, value)); \
(oword).u32[1] = (((oword).u32[1] \
& ~EFX_INPLACE_MASK32(32, 63, low, high)) \
| EFX_INSERT32(32, 63, low, high, value)); \
(oword).u32[2] = (((oword).u32[2] \
& ~EFX_INPLACE_MASK32(64, 95, low, high)) \
| EFX_INSERT32(64, 95, low, high, value)); \
(oword).u32[3] = (((oword).u32[3] \
& ~EFX_INPLACE_MASK32(96, 127, low, high)) \
| EFX_INSERT32(96, 127, low, high, value)); \
} while (0)
#define EFX_SET_QWORD32(qword, low, high, value) do { \
(qword).u32[0] = (((qword).u32[0] \
& ~EFX_INPLACE_MASK32(0, 31, low, high)) \
| EFX_INSERT32(0, 31, low, high, value)); \
(qword).u32[1] = (((qword).u32[1] \
& ~EFX_INPLACE_MASK32(32, 63, low, high)) \
| EFX_INSERT32(32, 63, low, high, value)); \
} while (0)
#define EFX_SET_DWORD32(dword, low, high, value) do { \
(dword).u32[0] = (((dword).u32[0] \
& ~EFX_INPLACE_MASK32(0, 31, low, high)) \
| EFX_INSERT32(0, 31, low, high, value)); \
} while (0)
#define EFX_SET_OWORD_FIELD64(oword, field, value) \
EFX_SET_OWORD64(oword, EFX_LOW_BIT(field), \
EFX_HIGH_BIT(field), value)
#define EFX_SET_QWORD_FIELD64(qword, field, value) \
EFX_SET_QWORD64(qword, EFX_LOW_BIT(field), \
EFX_HIGH_BIT(field), value)
#define EFX_SET_OWORD_FIELD32(oword, field, value) \
EFX_SET_OWORD32(oword, EFX_LOW_BIT(field), \
EFX_HIGH_BIT(field), value)
#define EFX_SET_QWORD_FIELD32(qword, field, value) \
EFX_SET_QWORD32(qword, EFX_LOW_BIT(field), \
EFX_HIGH_BIT(field), value)
#define EFX_SET_DWORD_FIELD(dword, field, value) \
EFX_SET_DWORD32(dword, EFX_LOW_BIT(field), \
EFX_HIGH_BIT(field), value)
#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)
/* Static initialiser */
#define EFX_OWORD32(a, b, c, d) \
{ .u32 = { cpu_to_le32(a), cpu_to_le32(b), \
cpu_to_le32(c), cpu_to_le32(d) } }
#endif /* EFX_BITFIELD_H */

View File

@@ -0,0 +1,328 @@
/****************************************************************************
* Driver for Solarflare Solarstorm network controllers and boards
* Copyright 2007-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 "net_driver.h"
#include "phy.h"
#include "boards.h"
#include "efx.h"
#include "workarounds.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_id_led(efx, bl->state);
bl->state = !bl->state;
if (bl->resubmit)
mod_timer(&bl->timer, jiffies + BLINK_INTERVAL);
}
static void board_blink(struct efx_nic *efx, bool 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 = true;
blinker->state = false;
setup_timer(&blinker->timer, blink_led_timer,
(unsigned long)efx);
mod_timer(&blinker->timer, jiffies + BLINK_INTERVAL);
} else {
blinker->resubmit = false;
if (blinker->timer.function)
del_timer_sync(&blinker->timer);
efx->board_info.init_leds(efx);
}
}
/*****************************************************************************
* Support for LM87 sensor chip used on several boards
*/
#define LM87_REG_ALARMS1 0x41
#define LM87_REG_ALARMS2 0x42
#define LM87_IN_LIMITS(nr, _min, _max) \
0x2B + (nr) * 2, _max, 0x2C + (nr) * 2, _min
#define LM87_AIN_LIMITS(nr, _min, _max) \
0x3B + (nr), _max, 0x1A + (nr), _min
#define LM87_TEMP_INT_LIMITS(_min, _max) \
0x39, _max, 0x3A, _min
#define LM87_TEMP_EXT1_LIMITS(_min, _max) \
0x37, _max, 0x38, _min
#define LM87_ALARM_TEMP_INT 0x10
#define LM87_ALARM_TEMP_EXT1 0x20
#if defined(CONFIG_SENSORS_LM87) || defined(CONFIG_SENSORS_LM87_MODULE)
static int efx_init_lm87(struct efx_nic *efx, struct i2c_board_info *info,
const u8 *reg_values)
{
struct i2c_client *client = i2c_new_device(&efx->i2c_adap, info);
int rc;
if (!client)
return -EIO;
while (*reg_values) {
u8 reg = *reg_values++;
u8 value = *reg_values++;
rc = i2c_smbus_write_byte_data(client, reg, value);
if (rc)
goto err;
}
efx->board_info.hwmon_client = client;
return 0;
err:
i2c_unregister_device(client);
return rc;
}
static void efx_fini_lm87(struct efx_nic *efx)
{
i2c_unregister_device(efx->board_info.hwmon_client);
}
static int efx_check_lm87(struct efx_nic *efx, unsigned mask)
{
struct i2c_client *client = efx->board_info.hwmon_client;
s32 alarms1, alarms2;
/* If link is up then do not monitor temperature */
if (EFX_WORKAROUND_7884(efx) && efx->link_up)
return 0;
alarms1 = i2c_smbus_read_byte_data(client, LM87_REG_ALARMS1);
alarms2 = i2c_smbus_read_byte_data(client, LM87_REG_ALARMS2);
if (alarms1 < 0)
return alarms1;
if (alarms2 < 0)
return alarms2;
alarms1 &= mask;
alarms2 &= mask >> 8;
if (alarms1 || alarms2) {
EFX_ERR(efx,
"LM87 detected a hardware failure (status %02x:%02x)"
"%s%s\n",
alarms1, alarms2,
(alarms1 & LM87_ALARM_TEMP_INT) ? " INTERNAL" : "",
(alarms1 & LM87_ALARM_TEMP_EXT1) ? " EXTERNAL" : "");
return -ERANGE;
}
return 0;
}
#else /* !CONFIG_SENSORS_LM87 */
static inline int
efx_init_lm87(struct efx_nic *efx, struct i2c_board_info *info,
const u8 *reg_values)
{
return 0;
}
static inline void efx_fini_lm87(struct efx_nic *efx)
{
}
static inline int efx_check_lm87(struct efx_nic *efx, unsigned mask)
{
return 0;
}
#endif /* CONFIG_SENSORS_LM87 */
/*****************************************************************************
* Support for the SFE4002
*
*/
static u8 sfe4002_lm87_channel = 0x03; /* use AIN not FAN inputs */
static const u8 sfe4002_lm87_regs[] = {
LM87_IN_LIMITS(0, 0x83, 0x91), /* 2.5V: 1.8V +/- 5% */
LM87_IN_LIMITS(1, 0x51, 0x5a), /* Vccp1: 1.2V +/- 5% */
LM87_IN_LIMITS(2, 0xb6, 0xca), /* 3.3V: 3.3V +/- 5% */
LM87_IN_LIMITS(3, 0xb0, 0xc9), /* 5V: 4.6-5.2V */
LM87_IN_LIMITS(4, 0xb0, 0xe0), /* 12V: 11-14V */
LM87_IN_LIMITS(5, 0x44, 0x4b), /* Vccp2: 1.0V +/- 5% */
LM87_AIN_LIMITS(0, 0xa0, 0xb2), /* AIN1: 1.66V +/- 5% */
LM87_AIN_LIMITS(1, 0x91, 0xa1), /* AIN2: 1.5V +/- 5% */
LM87_TEMP_INT_LIMITS(10, 60), /* board */
LM87_TEMP_EXT1_LIMITS(10, 70), /* Falcon */
0
};
static struct i2c_board_info sfe4002_hwmon_info = {
I2C_BOARD_INFO("lm87", 0x2e),
.platform_data = &sfe4002_lm87_channel,
};
/****************************************************************************/
/* 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 void 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);
}
static void sfe4002_set_id_led(struct efx_nic *efx, bool state)
{
xfp_set_led(efx, SFE4002_FAULT_LED, state ? QUAKE_LED_ON :
QUAKE_LED_OFF);
}
static int sfe4002_check_hw(struct efx_nic *efx)
{
/* A0 board rev. 4002s report a temperature fault the whole time
* (bad sensor) so we mask it out. */
unsigned alarm_mask =
(efx->board_info.major == 0 && efx->board_info.minor == 0) ?
~LM87_ALARM_TEMP_EXT1 : ~0;
return efx_check_lm87(efx, alarm_mask);
}
static int sfe4002_init(struct efx_nic *efx)
{
int rc = efx_init_lm87(efx, &sfe4002_hwmon_info, sfe4002_lm87_regs);
if (rc)
return rc;
efx->board_info.monitor = sfe4002_check_hw;
efx->board_info.init_leds = sfe4002_init_leds;
efx->board_info.set_id_led = sfe4002_set_id_led;
efx->board_info.blink = board_blink;
efx->board_info.fini = efx_fini_lm87;
return 0;
}
/*****************************************************************************
* Support for the SFN4112F
*
*/
static u8 sfn4112f_lm87_channel = 0x03; /* use AIN not FAN inputs */
static const u8 sfn4112f_lm87_regs[] = {
LM87_IN_LIMITS(0, 0x83, 0x91), /* 2.5V: 1.8V +/- 5% */
LM87_IN_LIMITS(1, 0x51, 0x5a), /* Vccp1: 1.2V +/- 5% */
LM87_IN_LIMITS(2, 0xb6, 0xca), /* 3.3V: 3.3V +/- 5% */
LM87_IN_LIMITS(4, 0xb0, 0xe0), /* 12V: 11-14V */
LM87_IN_LIMITS(5, 0x44, 0x4b), /* Vccp2: 1.0V +/- 5% */
LM87_AIN_LIMITS(1, 0x91, 0xa1), /* AIN2: 1.5V +/- 5% */
LM87_TEMP_INT_LIMITS(10, 60), /* board */
LM87_TEMP_EXT1_LIMITS(10, 70), /* Falcon */
0
};
static struct i2c_board_info sfn4112f_hwmon_info = {
I2C_BOARD_INFO("lm87", 0x2e),
.platform_data = &sfn4112f_lm87_channel,
};
#define SFN4112F_ACT_LED 0
#define SFN4112F_LINK_LED 1
static void sfn4112f_init_leds(struct efx_nic *efx)
{
xfp_set_led(efx, SFN4112F_ACT_LED,
QUAKE_LED_RXLINK | QUAKE_LED_LINK_ACT);
xfp_set_led(efx, SFN4112F_LINK_LED,
QUAKE_LED_RXLINK | QUAKE_LED_LINK_STAT);
}
static void sfn4112f_set_id_led(struct efx_nic *efx, bool state)
{
xfp_set_led(efx, SFN4112F_LINK_LED,
state ? QUAKE_LED_ON : QUAKE_LED_OFF);
}
static int sfn4112f_check_hw(struct efx_nic *efx)
{
/* Mask out unused sensors */
return efx_check_lm87(efx, ~0x48);
}
static int sfn4112f_init(struct efx_nic *efx)
{
int rc = efx_init_lm87(efx, &sfn4112f_hwmon_info, sfn4112f_lm87_regs);
if (rc)
return rc;
efx->board_info.monitor = sfn4112f_check_hw;
efx->board_info.init_leds = sfn4112f_init_leds;
efx->board_info.set_id_led = sfn4112f_set_id_led;
efx->board_info.blink = board_blink;
efx->board_info.fini = efx_fini_lm87;
return 0;
}
/* This will get expanded as board-specific details get moved out of the
* PHY drivers. */
struct efx_board_data {
enum efx_board_type type;
const char *ref_model;
const char *gen_type;
int (*init) (struct efx_nic *nic);
};
static struct efx_board_data board_data[] = {
{ EFX_BOARD_SFE4001, "SFE4001", "10GBASE-T adapter", sfe4001_init },
{ EFX_BOARD_SFE4002, "SFE4002", "XFP adapter", sfe4002_init },
{ EFX_BOARD_SFN4111T, "SFN4111T", "100/1000/10GBASE-T adapter",
sfn4111t_init },
{ EFX_BOARD_SFN4112F, "SFN4112F", "SFP+ adapter",
sfn4112f_init },
};
void efx_set_board_info(struct efx_nic *efx, u16 revision_info)
{
struct efx_board_data *data = NULL;
int i;
efx->board_info.type = BOARD_TYPE(revision_info);
efx->board_info.major = BOARD_MAJOR(revision_info);
efx->board_info.minor = BOARD_MINOR(revision_info);
for (i = 0; i < ARRAY_SIZE(board_data); i++)
if (board_data[i].type == efx->board_info.type)
data = &board_data[i];
if (data) {
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;
} else {
EFX_ERR(efx, "unknown board type %d\n", efx->board_info.type);
}
}

View File

@@ -0,0 +1,28 @@
/****************************************************************************
* Driver for Solarflare Solarstorm network controllers and boards
* Copyright 2007-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_BOARDS_H
#define EFX_BOARDS_H
/* Board IDs (must fit in 8 bits) */
enum efx_board_type {
EFX_BOARD_SFE4001 = 1,
EFX_BOARD_SFE4002 = 2,
EFX_BOARD_SFN4111T = 0x51,
EFX_BOARD_SFN4112F = 0x52,
};
extern void efx_set_board_info(struct efx_nic *efx, u16 revision_info);
/* SFE4001 (10GBASE-T) */
extern int sfe4001_init(struct efx_nic *efx);
/* SFN4111T (100/1000/10GBASE-T) */
extern int sfn4111t_init(struct efx_nic *efx);
#endif

2321
kernel/drivers/net/sfc/efx.c Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,87 @@
/****************************************************************************
* 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 netdev_tx_t 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, bool checksummed, bool 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 void efx_flush_queues(struct efx_nic *efx);
/* Ports */
extern void efx_stats_disable(struct efx_nic *efx);
extern void efx_stats_enable(struct efx_nic *efx);
extern void efx_reconfigure_port(struct efx_nic *efx);
extern void __efx_reconfigure_port(struct efx_nic *efx);
/* Reset handling */
extern void efx_reset_down(struct efx_nic *efx, enum reset_type method,
struct ethtool_cmd *ecmd);
extern int efx_reset_up(struct efx_nic *efx, enum reset_type method,
struct ethtool_cmd *ecmd, bool ok);
/* 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, bool rx_adaptive);
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, bool blink);
/* MTD */
#ifdef CONFIG_SFC_MTD
extern int efx_mtd_probe(struct efx_nic *efx);
extern void efx_mtd_rename(struct efx_nic *efx);
extern void efx_mtd_remove(struct efx_nic *efx);
#else
static inline int efx_mtd_probe(struct efx_nic *efx) { return 0; }
static inline void efx_mtd_rename(struct efx_nic *efx) {}
static inline void efx_mtd_remove(struct efx_nic *efx) {}
#endif
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 = true;
napi_schedule(&channel->napi_str);
}
#endif /* EFX_EFX_H */

View File

@@ -0,0 +1,104 @@
/****************************************************************************
* Driver for Solarflare Solarstorm network controllers and boards
* Copyright 2007-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_ENUM_H
#define EFX_ENUM_H
/**
* enum efx_loopback_mode - loopback modes
* @LOOPBACK_NONE: no loopback
* @LOOPBACK_GMAC: loopback within GMAC at unspecified level
* @LOOPBACK_XGMII: loopback within XMAC at XGMII level
* @LOOPBACK_XGXS: loopback within XMAC at XGXS level
* @LOOPBACK_XAUI: loopback within XMAC at XAUI level
* @LOOPBACK_GPHY: loopback within 1G PHY at unspecified level
* @LOOPBACK_PHYXS: loopback within 10G PHY at PHYXS level
* @LOOPBACK_PCS: loopback within 10G PHY at PCS level
* @LOOPBACK_PMAPMD: loopback within 10G PHY at PMAPMD level
* @LOOPBACK_NETWORK: reflecting loopback (even further than furthest!)
*/
/* Please keep in order and up-to-date w.r.t the following two #defines */
enum efx_loopback_mode {
LOOPBACK_NONE = 0,
LOOPBACK_GMAC = 1,
LOOPBACK_XGMII = 2,
LOOPBACK_XGXS = 3,
LOOPBACK_XAUI = 4,
LOOPBACK_GPHY = 5,
LOOPBACK_PHYXS = 6,
LOOPBACK_PCS = 7,
LOOPBACK_PMAPMD = 8,
LOOPBACK_NETWORK = 9,
LOOPBACK_MAX
};
#define LOOPBACK_TEST_MAX LOOPBACK_PMAPMD
extern const char *efx_loopback_mode_names[];
#define LOOPBACK_MODE_NAME(mode) \
STRING_TABLE_LOOKUP(mode, efx_loopback_mode)
#define LOOPBACK_MODE(efx) \
LOOPBACK_MODE_NAME(efx->loopback_mode)
/* These loopbacks occur within the controller */
#define LOOPBACKS_INTERNAL ((1 << LOOPBACK_GMAC) | \
(1 << LOOPBACK_XGMII)| \
(1 << LOOPBACK_XGXS) | \
(1 << LOOPBACK_XAUI))
#define LOOPBACK_MASK(_efx) \
(1 << (_efx)->loopback_mode)
#define LOOPBACK_INTERNAL(_efx) \
(!!(LOOPBACKS_INTERNAL & LOOPBACK_MASK(_efx)))
#define LOOPBACK_CHANGED(_from, _to, _mask) \
(!!((LOOPBACK_MASK(_from) ^ LOOPBACK_MASK(_to)) & (_mask)))
#define LOOPBACK_OUT_OF(_from, _to, _mask) \
((LOOPBACK_MASK(_from) & (_mask)) && !(LOOPBACK_MASK(_to) & (_mask)))
/*****************************************************************************/
/**
* 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_TX_WATCHDOG: reset due to TX watchdog
* @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_TX_WATCHDOG,
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 */

View File

@@ -0,0 +1,762 @@
/****************************************************************************
* 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/mdio.h>
#include <linux/rtnetlink.h>
#include "net_driver.h"
#include "workarounds.h"
#include "selftest.h"
#include "efx.h"
#include "ethtool.h"
#include "falcon.h"
#include "spi.h"
#include "mdio_10g.h"
const char *efx_loopback_mode_names[] = {
[LOOPBACK_NONE] = "NONE",
[LOOPBACK_GMAC] = "GMAC",
[LOOPBACK_XGMII] = "XGMII",
[LOOPBACK_XGXS] = "XGXS",
[LOOPBACK_XAUI] = "XAUI",
[LOOPBACK_GPHY] = "GPHY",
[LOOPBACK_PHYXS] = "PHYXS",
[LOOPBACK_PCS] = "PCS",
[LOOPBACK_PMAPMD] = "PMA/PMD",
[LOOPBACK_NETWORK] = "NETWORK",
};
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)
#define EFX_ETHTOOL_EEPROM_MAGIC 0xEFAB
/**************************************************************************
*
* Ethtool operations
*
**************************************************************************
*/
/* Identify device by flashing LEDs */
static int efx_ethtool_phys_id(struct net_device *net_dev, u32 count)
{
struct efx_nic *efx = netdev_priv(net_dev);
efx->board_info.blink(efx, 1);
set_current_state(TASK_INTERRUPTIBLE);
if (count)
schedule_timeout(count * HZ);
else
schedule();
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 = netdev_priv(net_dev);
mutex_lock(&efx->mac_lock);
efx->phy_op->get_settings(efx, ecmd);
mutex_unlock(&efx->mac_lock);
/* Falcon GMAC does not support 1000Mbps HD */
ecmd->supported &= ~SUPPORTED_1000baseT_Half;
return 0;
}
/* 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 = netdev_priv(net_dev);
int rc;
/* Falcon GMAC does not support 1000Mbps HD */
if (ecmd->speed == SPEED_1000 && ecmd->duplex != DUPLEX_FULL) {
EFX_LOG(efx, "rejecting unsupported 1000Mbps HD"
" setting\n");
return -EINVAL;
}
mutex_lock(&efx->mac_lock);
rc = efx->phy_op->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 = netdev_priv(net_dev);
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));
}
/**
* efx_fill_test - fill in an individual self-test entry
* @test_index: Index of the test
* @strings: Ethtool strings, or %NULL
* @data: Ethtool test results, or %NULL
* @test: Pointer to test result (used only if data != %NULL)
* @unit_format: Unit name format (e.g. "chan\%d")
* @unit_id: Unit id (e.g. 0 for "chan0")
* @test_format: Test name format (e.g. "loopback.\%s.tx.sent")
* @test_id: Test id (e.g. "PHYXS" for "loopback.PHYXS.tx_sent")
*
* Fill in an individual self-test entry.
*/
static void efx_fill_test(unsigned int test_index,
struct ethtool_string *strings, u64 *data,
int *test, const char *unit_format, int unit_id,
const char *test_format, const char *test_id)
{
struct ethtool_string unit_str, test_str;
/* Fill data value, if applicable */
if (data)
data[test_index] = *test;
/* Fill string, if applicable */
if (strings) {
if (strchr(unit_format, '%'))
snprintf(unit_str.name, sizeof(unit_str.name),
unit_format, unit_id);
else
strcpy(unit_str.name, unit_format);
snprintf(test_str.name, sizeof(test_str.name),
test_format, test_id);
snprintf(strings[test_index].name,
sizeof(strings[test_index].name),
"%-6s %-24s", unit_str.name, test_str.name);
}
}
#define EFX_CHANNEL_NAME(_channel) "chan%d", _channel->channel
#define EFX_TX_QUEUE_NAME(_tx_queue) "txq%d", _tx_queue->queue
#define EFX_RX_QUEUE_NAME(_rx_queue) "rxq%d", _rx_queue->queue
#define EFX_LOOPBACK_NAME(_mode, _counter) \
"loopback.%s." _counter, LOOPBACK_MODE_NAME(mode)
/**
* efx_fill_loopback_test - fill in a block of loopback self-test entries
* @efx: Efx NIC
* @lb_tests: Efx loopback self-test results structure
* @mode: Loopback test mode
* @test_index: Starting index of the test
* @strings: Ethtool strings, or %NULL
* @data: Ethtool test results, or %NULL
*/
static int efx_fill_loopback_test(struct efx_nic *efx,
struct efx_loopback_self_tests *lb_tests,
enum efx_loopback_mode mode,
unsigned int test_index,
struct ethtool_string *strings, u64 *data)
{
struct efx_tx_queue *tx_queue;
efx_for_each_tx_queue(tx_queue, efx) {
efx_fill_test(test_index++, strings, data,
&lb_tests->tx_sent[tx_queue->queue],
EFX_TX_QUEUE_NAME(tx_queue),
EFX_LOOPBACK_NAME(mode, "tx_sent"));
efx_fill_test(test_index++, strings, data,
&lb_tests->tx_done[tx_queue->queue],
EFX_TX_QUEUE_NAME(tx_queue),
EFX_LOOPBACK_NAME(mode, "tx_done"));
}
efx_fill_test(test_index++, strings, data,
&lb_tests->rx_good,
"rx", 0,
EFX_LOOPBACK_NAME(mode, "rx_good"));
efx_fill_test(test_index++, strings, data,
&lb_tests->rx_bad,
"rx", 0,
EFX_LOOPBACK_NAME(mode, "rx_bad"));
return test_index;
}
/**
* efx_ethtool_fill_self_tests - get self-test details
* @efx: Efx NIC
* @tests: Efx self-test results structure, or %NULL
* @strings: Ethtool strings, or %NULL
* @data: Ethtool test results, or %NULL
*/
static int efx_ethtool_fill_self_tests(struct efx_nic *efx,
struct efx_self_tests *tests,
struct ethtool_string *strings,
u64 *data)
{
struct efx_channel *channel;
unsigned int n = 0, i;
enum efx_loopback_mode mode;
efx_fill_test(n++, strings, data, &tests->mdio,
"core", 0, "mdio", NULL);
efx_fill_test(n++, strings, data, &tests->nvram,
"core", 0, "nvram", NULL);
efx_fill_test(n++, strings, data, &tests->interrupt,
"core", 0, "interrupt", NULL);
/* Event queues */
efx_for_each_channel(channel, efx) {
efx_fill_test(n++, strings, data,
&tests->eventq_dma[channel->channel],
EFX_CHANNEL_NAME(channel),
"eventq.dma", NULL);
efx_fill_test(n++, strings, data,
&tests->eventq_int[channel->channel],
EFX_CHANNEL_NAME(channel),
"eventq.int", NULL);
efx_fill_test(n++, strings, data,
&tests->eventq_poll[channel->channel],
EFX_CHANNEL_NAME(channel),
"eventq.poll", NULL);
}
efx_fill_test(n++, strings, data, &tests->registers,
"core", 0, "registers", NULL);
for (i = 0; i < efx->phy_op->num_tests; i++)
efx_fill_test(n++, strings, data, &tests->phy[i],
"phy", 0, efx->phy_op->test_names[i], NULL);
/* Loopback tests */
for (mode = LOOPBACK_NONE; mode <= LOOPBACK_TEST_MAX; mode++) {
if (!(efx->loopback_modes & (1 << mode)))
continue;
n = efx_fill_loopback_test(efx,
&tests->loopback[mode], mode, n,
strings, data);
}
return n;
}
static int efx_ethtool_get_sset_count(struct net_device *net_dev,
int string_set)
{
switch (string_set) {
case ETH_SS_STATS:
return EFX_ETHTOOL_NUM_STATS;
case ETH_SS_TEST:
return efx_ethtool_fill_self_tests(netdev_priv(net_dev),
NULL, NULL, NULL);
default:
return -EINVAL;
}
}
static void efx_ethtool_get_strings(struct net_device *net_dev,
u32 string_set, u8 *strings)
{
struct efx_nic *efx = netdev_priv(net_dev);
struct ethtool_string *ethtool_strings =
(struct ethtool_string *)strings;
int i;
switch (string_set) {
case 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));
break;
case ETH_SS_TEST:
efx_ethtool_fill_self_tests(efx, NULL,
ethtool_strings, NULL);
break;
default:
/* No other string sets */
break;
}
}
static void efx_ethtool_get_stats(struct net_device *net_dev,
struct ethtool_stats *stats,
u64 *data)
{
struct efx_nic *efx = netdev_priv(net_dev);
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 */
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_rx_csum(struct net_device *net_dev, u32 enable)
{
struct efx_nic *efx = netdev_priv(net_dev);
/* No way to stop the hardware doing the checks; we just
* ignore the result.
*/
efx->rx_checksum_enabled = !!enable;
return 0;
}
static u32 efx_ethtool_get_rx_csum(struct net_device *net_dev)
{
struct efx_nic *efx = netdev_priv(net_dev);
return efx->rx_checksum_enabled;
}
static void efx_ethtool_self_test(struct net_device *net_dev,
struct ethtool_test *test, u64 *data)
{
struct efx_nic *efx = netdev_priv(net_dev);
struct efx_self_tests efx_tests;
int already_up;
int rc;
ASSERT_RTNL();
if (efx->state != STATE_RUNNING) {
rc = -EIO;
goto fail1;
}
/* We need rx buffers and interrupts. */
already_up = (efx->net_dev->flags & IFF_UP);
if (!already_up) {
rc = dev_open(efx->net_dev);
if (rc) {
EFX_ERR(efx, "failed opening device.\n");
goto fail2;
}
}
memset(&efx_tests, 0, sizeof(efx_tests));
rc = efx_selftest(efx, &efx_tests, test->flags);
if (!already_up)
dev_close(efx->net_dev);
EFX_LOG(efx, "%s %sline self-tests\n",
rc == 0 ? "passed" : "failed",
(test->flags & ETH_TEST_FL_OFFLINE) ? "off" : "on");
fail2:
fail1:
/* Fill ethtool results structures */
efx_ethtool_fill_self_tests(efx, &efx_tests, NULL, data);
if (rc)
test->flags |= ETH_TEST_FL_FAILED;
}
/* Restart autonegotiation */
static int efx_ethtool_nway_reset(struct net_device *net_dev)
{
struct efx_nic *efx = netdev_priv(net_dev);
return mdio45_nway_restart(&efx->mdio);
}
static u32 efx_ethtool_get_link(struct net_device *net_dev)
{
struct efx_nic *efx = netdev_priv(net_dev);
return efx->link_up;
}
static int efx_ethtool_get_eeprom_len(struct net_device *net_dev)
{
struct efx_nic *efx = netdev_priv(net_dev);
struct efx_spi_device *spi = efx->spi_eeprom;
if (!spi)
return 0;
return min(spi->size, EFX_EEPROM_BOOTCONFIG_END) -
min(spi->size, EFX_EEPROM_BOOTCONFIG_START);
}
static int efx_ethtool_get_eeprom(struct net_device *net_dev,
struct ethtool_eeprom *eeprom, u8 *buf)
{
struct efx_nic *efx = netdev_priv(net_dev);
struct efx_spi_device *spi = efx->spi_eeprom;
size_t len;
int rc;
rc = mutex_lock_interruptible(&efx->spi_lock);
if (rc)
return rc;
rc = falcon_spi_read(spi, eeprom->offset + EFX_EEPROM_BOOTCONFIG_START,
eeprom->len, &len, buf);
mutex_unlock(&efx->spi_lock);
eeprom->magic = EFX_ETHTOOL_EEPROM_MAGIC;
eeprom->len = len;
return rc;
}
static int efx_ethtool_set_eeprom(struct net_device *net_dev,
struct ethtool_eeprom *eeprom, u8 *buf)
{
struct efx_nic *efx = netdev_priv(net_dev);
struct efx_spi_device *spi = efx->spi_eeprom;
size_t len;
int rc;
if (eeprom->magic != EFX_ETHTOOL_EEPROM_MAGIC)
return -EINVAL;
rc = mutex_lock_interruptible(&efx->spi_lock);
if (rc)
return rc;
rc = falcon_spi_write(spi, eeprom->offset + EFX_EEPROM_BOOTCONFIG_START,
eeprom->len, &len, buf);
mutex_unlock(&efx->spi_lock);
eeprom->len = len;
return rc;
}
static int efx_ethtool_get_coalesce(struct net_device *net_dev,
struct ethtool_coalesce *coalesce)
{
struct efx_nic *efx = netdev_priv(net_dev);
struct efx_tx_queue *tx_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;
}
}
coalesce->use_adaptive_rx_coalesce = efx->irq_rx_adaptive;
coalesce->rx_coalesce_usecs_irq = efx->irq_rx_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 = netdev_priv(net_dev);
struct efx_channel *channel;
struct efx_tx_queue *tx_queue;
unsigned tx_usecs, rx_usecs, adaptive;
if (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;
adaptive = coalesce->use_adaptive_rx_coalesce;
/* 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, adaptive);
/* 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 = netdev_priv(net_dev);
enum efx_fc_type wanted_fc;
bool reset;
wanted_fc = ((pause->rx_pause ? EFX_FC_RX : 0) |
(pause->tx_pause ? EFX_FC_TX : 0) |
(pause->autoneg ? EFX_FC_AUTO : 0));
if ((wanted_fc & EFX_FC_TX) && !(wanted_fc & EFX_FC_RX)) {
EFX_LOG(efx, "Flow control unsupported: tx ON rx OFF\n");
return -EINVAL;
}
if (!(efx->phy_op->mmds & MDIO_DEVS_AN) &&
(wanted_fc & EFX_FC_AUTO)) {
EFX_LOG(efx, "PHY does not support flow control "
"autonegotiation\n");
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 = (wanted_fc & EFX_FC_TX) && !(efx->wanted_fc & 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);
}
}
/* Try to push the pause parameters */
mutex_lock(&efx->mac_lock);
efx->wanted_fc = wanted_fc;
if (efx->phy_op->mmds & MDIO_DEVS_AN)
mdio45_ethtool_spauseparam_an(&efx->mdio, pause);
__efx_reconfigure_port(efx);
mutex_unlock(&efx->mac_lock);
return 0;
}
static void efx_ethtool_get_pauseparam(struct net_device *net_dev,
struct ethtool_pauseparam *pause)
{
struct efx_nic *efx = netdev_priv(net_dev);
pause->rx_pause = !!(efx->wanted_fc & EFX_FC_RX);
pause->tx_pause = !!(efx->wanted_fc & EFX_FC_TX);
pause->autoneg = !!(efx->wanted_fc & EFX_FC_AUTO);
}
const 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_eeprom_len = efx_ethtool_get_eeprom_len,
.get_eeprom = efx_ethtool_get_eeprom,
.set_eeprom = efx_ethtool_set_eeprom,
.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 = ethtool_op_set_tx_csum,
.get_sg = ethtool_op_get_sg,
.set_sg = ethtool_op_set_sg,
.get_tso = ethtool_op_get_tso,
.set_tso = ethtool_op_set_tso,
.get_flags = ethtool_op_get_flags,
.set_flags = ethtool_op_set_flags,
.get_sset_count = efx_ethtool_get_sset_count,
.self_test = efx_ethtool_self_test,
.get_strings = efx_ethtool_get_strings,
.phys_id = efx_ethtool_phys_id,
.get_ethtool_stats = efx_ethtool_get_stats,
};

View 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 const struct ethtool_ops efx_ethtool_ops;
#endif /* EFX_ETHTOOL_H */

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,145 @@
/****************************************************************************
* 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"
#include "efx.h"
/*
* Falcon hardware control
*/
enum falcon_revision {
FALCON_REV_A0 = 0,
FALCON_REV_A1 = 1,
FALCON_REV_B0 = 2,
};
static inline int falcon_rev(struct efx_nic *efx)
{
return 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 void 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 void 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 void 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_switch_mac(struct efx_nic *efx);
extern bool 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_sim_phy_event(struct efx_nic *efx);
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);
#define FALCON_IRQ_MOD_RESOLUTION 5
/* 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_flush_queues(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);
/* Tests */
struct falcon_nvconfig;
extern int falcon_read_nvram(struct efx_nic *efx,
struct falcon_nvconfig *nvconfig);
extern int falcon_test_registers(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 */

View File

@@ -0,0 +1,229 @@
/****************************************************************************
* 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 "mac.h"
#include "falcon_hwdefs.h"
#include "falcon_io.h"
#include "gmii.h"
/**************************************************************************
*
* MAC operations
*
*************************************************************************/
static void falcon_reconfigure_gmac(struct efx_nic *efx)
{
bool loopback, tx_fc, rx_fc, bytemode;
int if_mode;
unsigned int max_frame_len;
efx_oword_t reg;
/* Configuration register 1 */
tx_fc = (efx->link_fc & EFX_FC_TX) || !efx->link_fd;
rx_fc = !!(efx->link_fc & EFX_FC_RX);
loopback = (efx->loopback_mode == LOOPBACK_GMAC);
bytemode = (efx->link_speed == 1000);
EFX_POPULATE_OWORD_5(reg,
GM_LOOP, loopback,
GM_TX_EN, 1,
GM_TX_FC_EN, tx_fc,
GM_RX_EN, 1,
GM_RX_FC_EN, rx_fc);
falcon_write(efx, &reg, GM_CFG1_REG);
udelay(10);
/* Configuration register 2 */
if_mode = (bytemode) ? 2 : 1;
EFX_POPULATE_OWORD_5(reg,
GM_IF_MODE, if_mode,
GM_PAD_CRC_EN, 1,
GM_LEN_CHK, 1,
GM_FD, efx->link_fd,
GM_PAMBL_LEN, 0x7/*datasheet recommended */);
falcon_write(efx, &reg, GM_CFG2_REG);
udelay(10);
/* Max frame len register */
max_frame_len = EFX_MAX_FRAME_LEN(efx->net_dev->mtu);
EFX_POPULATE_OWORD_1(reg, GM_MAX_FLEN, max_frame_len);
falcon_write(efx, &reg, GM_MAX_FLEN_REG);
udelay(10);
/* FIFO configuration register 0 */
EFX_POPULATE_OWORD_5(reg,
GMF_FTFENREQ, 1,
GMF_STFENREQ, 1,
GMF_FRFENREQ, 1,
GMF_SRFENREQ, 1,
GMF_WTMENREQ, 1);
falcon_write(efx, &reg, GMF_CFG0_REG);
udelay(10);
/* FIFO configuration register 1 */
EFX_POPULATE_OWORD_2(reg,
GMF_CFGFRTH, 0x12,
GMF_CFGXOFFRTX, 0xffff);
falcon_write(efx, &reg, GMF_CFG1_REG);
udelay(10);
/* FIFO configuration register 2 */
EFX_POPULATE_OWORD_2(reg,
GMF_CFGHWM, 0x3f,
GMF_CFGLWM, 0xa);
falcon_write(efx, &reg, GMF_CFG2_REG);
udelay(10);
/* FIFO configuration register 3 */
EFX_POPULATE_OWORD_2(reg,
GMF_CFGHWMFT, 0x1c,
GMF_CFGFTTH, 0x08);
falcon_write(efx, &reg, GMF_CFG3_REG);
udelay(10);
/* FIFO configuration register 4 */
EFX_POPULATE_OWORD_1(reg, GMF_HSTFLTRFRM_PAUSE, 1);
falcon_write(efx, &reg, GMF_CFG4_REG);
udelay(10);
/* FIFO configuration register 5 */
falcon_read(efx, &reg, GMF_CFG5_REG);
EFX_SET_OWORD_FIELD(reg, GMF_CFGBYTMODE, bytemode);
EFX_SET_OWORD_FIELD(reg, GMF_CFGHDPLX, !efx->link_fd);
EFX_SET_OWORD_FIELD(reg, GMF_HSTDRPLT64, !efx->link_fd);
EFX_SET_OWORD_FIELD(reg, GMF_HSTFLTRFRMDC_PAUSE, 0);
falcon_write(efx, &reg, GMF_CFG5_REG);
udelay(10);
/* MAC address */
EFX_POPULATE_OWORD_4(reg,
GM_HWADDR_5, efx->net_dev->dev_addr[5],
GM_HWADDR_4, efx->net_dev->dev_addr[4],
GM_HWADDR_3, efx->net_dev->dev_addr[3],
GM_HWADDR_2, efx->net_dev->dev_addr[2]);
falcon_write(efx, &reg, GM_ADR1_REG);
udelay(10);
EFX_POPULATE_OWORD_2(reg,
GM_HWADDR_1, efx->net_dev->dev_addr[1],
GM_HWADDR_0, efx->net_dev->dev_addr[0]);
falcon_write(efx, &reg, GM_ADR2_REG);
udelay(10);
falcon_reconfigure_mac_wrapper(efx);
}
static void falcon_update_stats_gmac(struct efx_nic *efx)
{
struct efx_mac_stats *mac_stats = &efx->mac_stats;
unsigned long old_rx_pause, old_tx_pause;
unsigned long new_rx_pause, new_tx_pause;
int rc;
rc = falcon_dma_stats(efx, GDmaDone_offset);
if (rc)
return;
/* Pause frames are erroneously counted as errors (SFC bug 3269) */
old_rx_pause = mac_stats->rx_pause;
old_tx_pause = mac_stats->tx_pause;
/* Update MAC stats from DMAed values */
FALCON_STAT(efx, GRxGoodOct, rx_good_bytes);
FALCON_STAT(efx, GRxBadOct, rx_bad_bytes);
FALCON_STAT(efx, GRxMissPkt, rx_missed);
FALCON_STAT(efx, GRxFalseCRS, rx_false_carrier);
FALCON_STAT(efx, GRxPausePkt, rx_pause);
FALCON_STAT(efx, GRxBadPkt, rx_bad);
FALCON_STAT(efx, GRxUcastPkt, rx_unicast);
FALCON_STAT(efx, GRxMcastPkt, rx_multicast);
FALCON_STAT(efx, GRxBcastPkt, rx_broadcast);
FALCON_STAT(efx, GRxGoodLt64Pkt, rx_good_lt64);
FALCON_STAT(efx, GRxBadLt64Pkt, rx_bad_lt64);
FALCON_STAT(efx, GRx64Pkt, rx_64);
FALCON_STAT(efx, GRx65to127Pkt, rx_65_to_127);
FALCON_STAT(efx, GRx128to255Pkt, rx_128_to_255);
FALCON_STAT(efx, GRx256to511Pkt, rx_256_to_511);
FALCON_STAT(efx, GRx512to1023Pkt, rx_512_to_1023);
FALCON_STAT(efx, GRx1024to15xxPkt, rx_1024_to_15xx);
FALCON_STAT(efx, GRx15xxtoJumboPkt, rx_15xx_to_jumbo);
FALCON_STAT(efx, GRxGtJumboPkt, rx_gtjumbo);
FALCON_STAT(efx, GRxFcsErr64to15xxPkt, rx_bad_64_to_15xx);
FALCON_STAT(efx, GRxFcsErr15xxtoJumboPkt, rx_bad_15xx_to_jumbo);
FALCON_STAT(efx, GRxFcsErrGtJumboPkt, rx_bad_gtjumbo);
FALCON_STAT(efx, GTxGoodBadOct, tx_bytes);
FALCON_STAT(efx, GTxGoodOct, tx_good_bytes);
FALCON_STAT(efx, GTxSglColPkt, tx_single_collision);
FALCON_STAT(efx, GTxMultColPkt, tx_multiple_collision);
FALCON_STAT(efx, GTxExColPkt, tx_excessive_collision);
FALCON_STAT(efx, GTxDefPkt, tx_deferred);
FALCON_STAT(efx, GTxLateCol, tx_late_collision);
FALCON_STAT(efx, GTxExDefPkt, tx_excessive_deferred);
FALCON_STAT(efx, GTxPausePkt, tx_pause);
FALCON_STAT(efx, GTxBadPkt, tx_bad);
FALCON_STAT(efx, GTxUcastPkt, tx_unicast);
FALCON_STAT(efx, GTxMcastPkt, tx_multicast);
FALCON_STAT(efx, GTxBcastPkt, tx_broadcast);
FALCON_STAT(efx, GTxLt64Pkt, tx_lt64);
FALCON_STAT(efx, GTx64Pkt, tx_64);
FALCON_STAT(efx, GTx65to127Pkt, tx_65_to_127);
FALCON_STAT(efx, GTx128to255Pkt, tx_128_to_255);
FALCON_STAT(efx, GTx256to511Pkt, tx_256_to_511);
FALCON_STAT(efx, GTx512to1023Pkt, tx_512_to_1023);
FALCON_STAT(efx, GTx1024to15xxPkt, tx_1024_to_15xx);
FALCON_STAT(efx, GTx15xxtoJumboPkt, tx_15xx_to_jumbo);
FALCON_STAT(efx, GTxGtJumboPkt, tx_gtjumbo);
FALCON_STAT(efx, GTxNonTcpUdpPkt, tx_non_tcpudp);
FALCON_STAT(efx, GTxMacSrcErrPkt, tx_mac_src_error);
FALCON_STAT(efx, GTxIpSrcErrPkt, tx_ip_src_error);
/* Pause frames are erroneously counted as errors (SFC bug 3269) */
new_rx_pause = mac_stats->rx_pause;
new_tx_pause = mac_stats->tx_pause;
mac_stats->rx_bad -= (new_rx_pause - old_rx_pause);
mac_stats->tx_bad -= (new_tx_pause - old_tx_pause);
/* Derive stats that the MAC doesn't provide directly */
mac_stats->tx_bad_bytes =
mac_stats->tx_bytes - mac_stats->tx_good_bytes;
mac_stats->tx_packets =
mac_stats->tx_lt64 + mac_stats->tx_64 +
mac_stats->tx_65_to_127 + mac_stats->tx_128_to_255 +
mac_stats->tx_256_to_511 + mac_stats->tx_512_to_1023 +
mac_stats->tx_1024_to_15xx + mac_stats->tx_15xx_to_jumbo +
mac_stats->tx_gtjumbo;
mac_stats->tx_collision =
mac_stats->tx_single_collision +
mac_stats->tx_multiple_collision +
mac_stats->tx_excessive_collision +
mac_stats->tx_late_collision;
mac_stats->rx_bytes =
mac_stats->rx_good_bytes + mac_stats->rx_bad_bytes;
mac_stats->rx_packets =
mac_stats->rx_good_lt64 + mac_stats->rx_bad_lt64 +
mac_stats->rx_64 + mac_stats->rx_65_to_127 +
mac_stats->rx_128_to_255 + mac_stats->rx_256_to_511 +
mac_stats->rx_512_to_1023 + mac_stats->rx_1024_to_15xx +
mac_stats->rx_15xx_to_jumbo + mac_stats->rx_gtjumbo;
mac_stats->rx_good = mac_stats->rx_packets - mac_stats->rx_bad;
mac_stats->rx_lt64 = mac_stats->rx_good_lt64 + mac_stats->rx_bad_lt64;
}
struct efx_mac_operations falcon_gmac_operations = {
.reconfigure = falcon_reconfigure_gmac,
.update_stats = falcon_update_stats_gmac,
.irq = efx_port_dummy_op_void,
.poll = efx_port_dummy_op_void,
};

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,258 @@
/****************************************************************************
* 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>
/**************************************************************************
*
* 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
#ifdef FALCON_USE_QWORD_IO
static inline void _falcon_writeq(struct efx_nic *efx, __le64 value,
unsigned int reg)
{
__raw_writeq((__force u64)value, efx->membase + reg);
}
static inline __le64 _falcon_readq(struct efx_nic *efx, unsigned int reg)
{
return (__force __le64)__raw_readq(efx->membase + reg);
}
#endif
static inline void _falcon_writel(struct efx_nic *efx, __le32 value,
unsigned int reg)
{
__raw_writel((__force u32)value, efx->membase + reg);
}
static inline __le32 _falcon_readl(struct efx_nic *efx, unsigned int reg)
{
return (__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. If writing to page 0, 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 = 0;
if (page == 0)
spin_lock_irqsave(&efx->biu_lock, flags);
falcon_writel(efx, value, FALCON_PAGED_REG(page, reg));
if (page == 0)
spin_unlock_irqrestore(&efx->biu_lock, flags);
}
#endif /* EFX_FALCON_IO_H */

View File

@@ -0,0 +1,378 @@
/****************************************************************************
* 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 "mdio_10g.h"
#include "phy.h"
#include "boards.h"
#include "workarounds.h"
/**************************************************************************
*
* MAC operations
*
*************************************************************************/
/* Configure the XAUI driver that is an output from Falcon */
static void falcon_setup_xaui(struct efx_nic *efx)
{
efx_oword_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_read(efx, &sdctl, XX_SD_CTL_REG);
EFX_SET_OWORD_FIELD(sdctl, XX_HIDRVD, XX_SD_CTL_DRV_DEFAULT);
EFX_SET_OWORD_FIELD(sdctl, XX_LODRVD, XX_SD_CTL_DRV_DEFAULT);
EFX_SET_OWORD_FIELD(sdctl, XX_HIDRVC, XX_SD_CTL_DRV_DEFAULT);
EFX_SET_OWORD_FIELD(sdctl, XX_LODRVC, XX_SD_CTL_DRV_DEFAULT);
EFX_SET_OWORD_FIELD(sdctl, XX_HIDRVB, XX_SD_CTL_DRV_DEFAULT);
EFX_SET_OWORD_FIELD(sdctl, XX_LODRVB, XX_SD_CTL_DRV_DEFAULT);
EFX_SET_OWORD_FIELD(sdctl, XX_HIDRVA, XX_SD_CTL_DRV_DEFAULT);
EFX_SET_OWORD_FIELD(sdctl, XX_LODRVA, XX_SD_CTL_DRV_DEFAULT);
falcon_write(efx, &sdctl, XX_SD_CTL_REG);
EFX_POPULATE_OWORD_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_write(efx, &txdrv, XX_TXDRV_CTL_REG);
}
int falcon_reset_xaui(struct efx_nic *efx)
{
efx_oword_t reg;
int count;
/* Start reset sequence */
EFX_POPULATE_DWORD_1(reg, XX_RST_XX_EN, 1);
falcon_write(efx, &reg, XX_PWR_RST_REG);
/* Wait up to 10 ms for completion, then reinitialise */
for (count = 0; count < 1000; count++) {
falcon_read(efx, &reg, XX_PWR_RST_REG);
if (EFX_OWORD_FIELD(reg, XX_RST_XX_EN) == 0 &&
EFX_OWORD_FIELD(reg, XX_SD_RST_ACT) == 0) {
falcon_setup_xaui(efx);
return 0;
}
udelay(10);
}
EFX_ERR(efx, "timed out waiting for XAUI/XGXS reset\n");
return -ETIMEDOUT;
}
static void falcon_mask_status_intr(struct efx_nic *efx, bool enable)
{
efx_oword_t reg;
if ((falcon_rev(efx) != FALCON_REV_B0) || LOOPBACK_INTERNAL(efx))
return;
/* We expect xgmii faults if the wireside link is up */
if (!EFX_WORKAROUND_5147(efx) || !efx->link_up)
return;
/* We can only use this interrupt to signal the negative edge of
* xaui_align [we have to poll the positive edge]. */
if (!efx->mac_up)
return;
/* Flush the ISR */
if (enable)
falcon_read(efx, &reg, XM_MGT_INT_REG_B0);
EFX_POPULATE_OWORD_2(reg,
XM_MSK_RMTFLT, !enable,
XM_MSK_LCLFLT, !enable);
falcon_write(efx, &reg, XM_MGT_INT_MSK_REG_B0);
}
/* Get status of XAUI link */
bool falcon_xaui_link_ok(struct efx_nic *efx)
{
efx_oword_t reg;
bool align_done, link_ok = false;
int sync_status;
if (LOOPBACK_INTERNAL(efx))
return true;
/* Read link status */
falcon_read(efx, &reg, XX_CORE_STAT_REG);
align_done = EFX_OWORD_FIELD(reg, XX_ALIGN_DONE);
sync_status = EFX_OWORD_FIELD(reg, XX_SYNC_STAT);
if (align_done && (sync_status == XX_SYNC_STAT_DECODE_SYNCED))
link_ok = true;
/* Clear link status ready for next read */
EFX_SET_OWORD_FIELD(reg, XX_COMMA_DET, XX_COMMA_DET_RESET);
EFX_SET_OWORD_FIELD(reg, XX_CHARERR, XX_CHARERR_RESET);
EFX_SET_OWORD_FIELD(reg, XX_DISPERR, XX_DISPERR_RESET);
falcon_write(efx, &reg, XX_CORE_STAT_REG);
/* If the link is up, then check the phy side of the xaui link */
if (efx->link_up && link_ok)
if (efx->phy_op->mmds & (1 << MDIO_MMD_PHYXS))
link_ok = efx_mdio_phyxgxs_lane_sync(efx);
return link_ok;
}
static void falcon_reconfigure_xmac_core(struct efx_nic *efx)
{
unsigned int max_frame_len;
efx_oword_t reg;
bool rx_fc = !!(efx->link_fc & EFX_FC_RX);
/* 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_write(efx, &reg, XM_GLB_CFG_REG);
/* 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_write(efx, &reg, XM_TX_CFG_REG);
/* 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_write(efx, &reg, XM_RX_CFG_REG);
/* 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_write(efx, &reg, XM_RX_PARAM_REG);
EFX_POPULATE_DWORD_2(reg,
XM_MAX_TX_FRM_SIZE, max_frame_len,
XM_TX_JUMBO_MODE, 1);
falcon_write(efx, &reg, XM_TX_PARAM_REG);
EFX_POPULATE_DWORD_2(reg,
XM_PAUSE_TIME, 0xfffe, /* MAX PAUSE TIME */
XM_DIS_FCNTL, !rx_fc);
falcon_write(efx, &reg, XM_FC_REG);
/* 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_write(efx, &reg, XM_ADR_LO_REG);
EFX_POPULATE_DWORD_2(reg,
XM_ADR_4, efx->net_dev->dev_addr[4],
XM_ADR_5, efx->net_dev->dev_addr[5]);
falcon_write(efx, &reg, XM_ADR_HI_REG);
}
static void falcon_reconfigure_xgxs_core(struct efx_nic *efx)
{
efx_oword_t reg;
bool xgxs_loopback = (efx->loopback_mode == LOOPBACK_XGXS);
bool xaui_loopback = (efx->loopback_mode == LOOPBACK_XAUI);
bool xgmii_loopback = (efx->loopback_mode == LOOPBACK_XGMII);
/* XGXS block is flaky and will need to be reset if moving
* into our out of XGMII, XGXS or XAUI loopbacks. */
if (EFX_WORKAROUND_5147(efx)) {
bool old_xgmii_loopback, old_xgxs_loopback, old_xaui_loopback;
bool reset_xgxs;
falcon_read(efx, &reg, XX_CORE_STAT_REG);
old_xgxs_loopback = EFX_OWORD_FIELD(reg, XX_XGXS_LB_EN);
old_xgmii_loopback = EFX_OWORD_FIELD(reg, XX_XGMII_LB_EN);
falcon_read(efx, &reg, XX_SD_CTL_REG);
old_xaui_loopback = EFX_OWORD_FIELD(reg, XX_LPBKA);
/* The PHY driver may have turned XAUI off */
reset_xgxs = ((xgxs_loopback != old_xgxs_loopback) ||
(xaui_loopback != old_xaui_loopback) ||
(xgmii_loopback != old_xgmii_loopback));
if (reset_xgxs)
falcon_reset_xaui(efx);
}
falcon_read(efx, &reg, XX_CORE_STAT_REG);
EFX_SET_OWORD_FIELD(reg, XX_FORCE_SIG,
(xgxs_loopback || xaui_loopback) ?
XX_FORCE_SIG_DECODE_FORCED : 0);
EFX_SET_OWORD_FIELD(reg, XX_XGXS_LB_EN, xgxs_loopback);
EFX_SET_OWORD_FIELD(reg, XX_XGMII_LB_EN, xgmii_loopback);
falcon_write(efx, &reg, XX_CORE_STAT_REG);
falcon_read(efx, &reg, XX_SD_CTL_REG);
EFX_SET_OWORD_FIELD(reg, XX_LPBKD, xaui_loopback);
EFX_SET_OWORD_FIELD(reg, XX_LPBKC, xaui_loopback);
EFX_SET_OWORD_FIELD(reg, XX_LPBKB, xaui_loopback);
EFX_SET_OWORD_FIELD(reg, XX_LPBKA, xaui_loopback);
falcon_write(efx, &reg, XX_SD_CTL_REG);
}
/* 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 void falcon_check_xaui_link_up(struct efx_nic *efx, int tries)
{
efx->mac_up = falcon_xaui_link_ok(efx);
if ((efx->loopback_mode == LOOPBACK_NETWORK) ||
efx_phy_mode_disabled(efx->phy_mode))
/* XAUI link is expected to be down */
return;
while (!efx->mac_up && tries) {
EFX_LOG(efx, "bashing xaui\n");
falcon_reset_xaui(efx);
udelay(200);
efx->mac_up = falcon_xaui_link_ok(efx);
--tries;
}
}
static void falcon_reconfigure_xmac(struct efx_nic *efx)
{
falcon_mask_status_intr(efx, false);
falcon_reconfigure_xgxs_core(efx);
falcon_reconfigure_xmac_core(efx);
falcon_reconfigure_mac_wrapper(efx);
falcon_check_xaui_link_up(efx, 5);
falcon_mask_status_intr(efx, true);
}
static 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->tx_control * 64);
mac_stats->rx_bad_bytes =
(mac_stats->rx_bytes - mac_stats->rx_good_bytes -
mac_stats->rx_control * 64);
}
static void falcon_xmac_irq(struct efx_nic *efx)
{
/* The XGMII link has a transient fault, which indicates either:
* - there's a transient xgmii fault
* - falcon's end of the xaui link may need a kick
* - the wire-side link may have gone down, but the lasi/poll()
* hasn't noticed yet.
*
* We only want to even bother polling XAUI if we're confident it's
* not (1) or (3). In both cases, the only reliable way to spot this
* is to wait a bit. We do this here by forcing the mac link state
* to down, and waiting for the mac poll to come round and check
*/
efx->mac_up = false;
}
static void falcon_poll_xmac(struct efx_nic *efx)
{
if (!EFX_WORKAROUND_5147(efx) || !efx->link_up || efx->mac_up)
return;
falcon_mask_status_intr(efx, false);
falcon_check_xaui_link_up(efx, 1);
falcon_mask_status_intr(efx, true);
}
struct efx_mac_operations falcon_xmac_operations = {
.reconfigure = falcon_reconfigure_xmac,
.update_stats = falcon_update_stats_xmac,
.irq = falcon_xmac_irq,
.poll = falcon_poll_xmac,
};

View File

@@ -0,0 +1,60 @@
/****************************************************************************
* 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_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 */
#endif /* EFX_GMII_H */

View File

@@ -0,0 +1,19 @@
/****************************************************************************
* 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_MAC_H
#define EFX_MAC_H
#include "net_driver.h"
extern struct efx_mac_operations falcon_gmac_operations;
extern struct efx_mac_operations falcon_xmac_operations;
#endif

View File

@@ -0,0 +1,351 @@
/****************************************************************************
* 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"
#include "workarounds.h"
unsigned efx_mdio_id_oui(u32 id)
{
unsigned oui = 0;
int i;
/* The bits of the OUI are designated a..x, with a=0 and b variable.
* In the id register c is the MSB but the OUI is conventionally
* written as bytes h..a, p..i, x..q. Reorder the bits accordingly. */
for (i = 0; i < 22; ++i)
if (id & (1 << (i + 10)))
oui |= 1 << (i ^ 7);
return oui;
}
int efx_mdio_reset_mmd(struct efx_nic *port, int mmd,
int spins, int spintime)
{
u32 ctrl;
/* Catch callers passing values in the wrong units (or just silly) */
EFX_BUG_ON_PARANOID(spins * spintime >= 5000);
efx_mdio_write(port, mmd, MDIO_CTRL1, MDIO_CTRL1_RESET);
/* Wait for the reset bit to clear. */
do {
msleep(spintime);
ctrl = efx_mdio_read(port, mmd, MDIO_CTRL1);
spins--;
} while (spins && (ctrl & MDIO_CTRL1_RESET));
return spins ? spins : -ETIMEDOUT;
}
static int efx_mdio_check_mmd(struct efx_nic *efx, int mmd, int fault_fatal)
{
int status;
if (LOOPBACK_INTERNAL(efx))
return 0;
if (mmd != MDIO_MMD_AN) {
/* Read MMD STATUS2 to check it is responding. */
status = efx_mdio_read(efx, mmd, MDIO_STAT2);
if ((status & MDIO_STAT2_DEVPRST) != MDIO_STAT2_DEVPRST_VAL) {
EFX_ERR(efx, "PHY MMD %d not responding.\n", mmd);
return -EIO;
}
}
/* Read MMD STATUS 1 to check for fault. */
status = efx_mdio_read(efx, mmd, MDIO_STAT1);
if (status & MDIO_STAT1_FAULT) {
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 efx_mdio_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 = efx_mdio_read(efx, mmd, MDIO_CTRL1);
if (stat < 0) {
EFX_ERR(efx, "failed to read status of"
" MMD %d\n", mmd);
return -EIO;
}
if (stat & MDIO_CTRL1_RESET)
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 efx_mdio_check_mmds(struct efx_nic *efx,
unsigned int mmd_mask, unsigned int fatal_mask)
{
int mmd = 0, probe_mmd, devs1, devs2;
u32 devices;
/* 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_DEVS_PHYXS) ? MDIO_MMD_PHYXS :
__ffs(mmd_mask);
/* Check all the expected MMDs are present */
devs1 = efx_mdio_read(efx, probe_mmd, MDIO_DEVS1);
devs2 = efx_mdio_read(efx, probe_mmd, MDIO_DEVS2);
if (devs1 < 0 || devs2 < 0) {
EFX_ERR(efx, "failed to read devices present\n");
return -EIO;
}
devices = devs1 | (devs2 << 16);
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 (efx_mdio_check_mmd(efx, mmd, fault_fatal))
return -EIO;
}
mmd_mask = mmd_mask >> 1;
fatal_mask = fatal_mask >> 1;
mmd++;
}
return 0;
}
bool efx_mdio_links_ok(struct efx_nic *efx, unsigned int mmd_mask)
{
/* If the port is in loopback, then we should only consider a subset
* of mmd's */
if (LOOPBACK_INTERNAL(efx))
return true;
else if (efx->loopback_mode == LOOPBACK_NETWORK)
return false;
else if (efx_phy_mode_disabled(efx->phy_mode))
return false;
else if (efx->loopback_mode == LOOPBACK_PHYXS)
mmd_mask &= ~(MDIO_DEVS_PHYXS |
MDIO_DEVS_PCS |
MDIO_DEVS_PMAPMD |
MDIO_DEVS_AN);
else if (efx->loopback_mode == LOOPBACK_PCS)
mmd_mask &= ~(MDIO_DEVS_PCS |
MDIO_DEVS_PMAPMD |
MDIO_DEVS_AN);
else if (efx->loopback_mode == LOOPBACK_PMAPMD)
mmd_mask &= ~(MDIO_DEVS_PMAPMD |
MDIO_DEVS_AN);
return mdio45_links_ok(&efx->mdio, mmd_mask);
}
void efx_mdio_transmit_disable(struct efx_nic *efx)
{
efx_mdio_set_flag(efx, MDIO_MMD_PMAPMD,
MDIO_PMA_TXDIS, MDIO_PMD_TXDIS_GLOBAL,
efx->phy_mode & PHY_MODE_TX_DISABLED);
}
void efx_mdio_phy_reconfigure(struct efx_nic *efx)
{
efx_mdio_set_flag(efx, MDIO_MMD_PMAPMD,
MDIO_CTRL1, MDIO_PMA_CTRL1_LOOPBACK,
efx->loopback_mode == LOOPBACK_PMAPMD);
efx_mdio_set_flag(efx, MDIO_MMD_PCS,
MDIO_CTRL1, MDIO_PCS_CTRL1_LOOPBACK,
efx->loopback_mode == LOOPBACK_PCS);
efx_mdio_set_flag(efx, MDIO_MMD_PHYXS,
MDIO_CTRL1, MDIO_PHYXS_CTRL1_LOOPBACK,
efx->loopback_mode == LOOPBACK_NETWORK);
}
static void efx_mdio_set_mmd_lpower(struct efx_nic *efx,
int lpower, int mmd)
{
int stat = efx_mdio_read(efx, mmd, MDIO_STAT1);
EFX_TRACE(efx, "Setting low power mode for MMD %d to %d\n",
mmd, lpower);
if (stat & MDIO_STAT1_LPOWERABLE) {
efx_mdio_set_flag(efx, mmd, MDIO_CTRL1,
MDIO_CTRL1_LPOWER, lpower);
}
}
void efx_mdio_set_mmds_lpower(struct efx_nic *efx,
int low_power, unsigned int mmd_mask)
{
int mmd = 0;
mmd_mask &= ~MDIO_DEVS_AN;
while (mmd_mask) {
if (mmd_mask & 1)
efx_mdio_set_mmd_lpower(efx, low_power, mmd);
mmd_mask = (mmd_mask >> 1);
mmd++;
}
}
/**
* efx_mdio_set_settings - Set (some of) the PHY settings over MDIO.
* @efx: Efx NIC
* @ecmd: New settings
*/
int efx_mdio_set_settings(struct efx_nic *efx, struct ethtool_cmd *ecmd)
{
struct ethtool_cmd prev;
u32 required;
int reg;
efx->phy_op->get_settings(efx, &prev);
if (ecmd->advertising == prev.advertising &&
ecmd->speed == prev.speed &&
ecmd->duplex == prev.duplex &&
ecmd->port == prev.port &&
ecmd->autoneg == prev.autoneg)
return 0;
/* We can only change these settings for -T PHYs */
if (prev.port != PORT_TP || ecmd->port != PORT_TP)
return -EINVAL;
/* Check that PHY supports these settings */
if (ecmd->autoneg) {
required = SUPPORTED_Autoneg;
} else if (ecmd->duplex) {
switch (ecmd->speed) {
case SPEED_10: required = SUPPORTED_10baseT_Full; break;
case SPEED_100: required = SUPPORTED_100baseT_Full; break;
default: return -EINVAL;
}
} else {
switch (ecmd->speed) {
case SPEED_10: required = SUPPORTED_10baseT_Half; break;
case SPEED_100: required = SUPPORTED_100baseT_Half; break;
default: return -EINVAL;
}
}
required |= ecmd->advertising;
if (required & ~prev.supported)
return -EINVAL;
if (ecmd->autoneg) {
bool xnp = (ecmd->advertising & ADVERTISED_10000baseT_Full
|| EFX_WORKAROUND_13204(efx));
/* Set up the base page */
reg = ADVERTISE_CSMA;
if (ecmd->advertising & ADVERTISED_10baseT_Half)
reg |= ADVERTISE_10HALF;
if (ecmd->advertising & ADVERTISED_10baseT_Full)
reg |= ADVERTISE_10FULL;
if (ecmd->advertising & ADVERTISED_100baseT_Half)
reg |= ADVERTISE_100HALF;
if (ecmd->advertising & ADVERTISED_100baseT_Full)
reg |= ADVERTISE_100FULL;
if (xnp)
reg |= ADVERTISE_RESV;
else if (ecmd->advertising & (ADVERTISED_1000baseT_Half |
ADVERTISED_1000baseT_Full))
reg |= ADVERTISE_NPAGE;
reg |= mii_advertise_flowctrl(efx->wanted_fc);
efx_mdio_write(efx, MDIO_MMD_AN, MDIO_AN_ADVERTISE, reg);
/* Set up the (extended) next page if necessary */
if (efx->phy_op->set_npage_adv)
efx->phy_op->set_npage_adv(efx, ecmd->advertising);
/* Enable and restart AN */
reg = efx_mdio_read(efx, MDIO_MMD_AN, MDIO_CTRL1);
reg |= MDIO_AN_CTRL1_ENABLE;
if (!(EFX_WORKAROUND_15195(efx) &&
LOOPBACK_MASK(efx) & efx->phy_op->loopbacks))
reg |= MDIO_AN_CTRL1_RESTART;
if (xnp)
reg |= MDIO_AN_CTRL1_XNP;
else
reg &= ~MDIO_AN_CTRL1_XNP;
efx_mdio_write(efx, MDIO_MMD_AN, MDIO_CTRL1, reg);
} else {
/* Disable AN */
efx_mdio_set_flag(efx, MDIO_MMD_AN, MDIO_CTRL1,
MDIO_AN_CTRL1_ENABLE, false);
/* Set the basic control bits */
reg = efx_mdio_read(efx, MDIO_MMD_PMAPMD, MDIO_CTRL1);
reg &= ~(MDIO_CTRL1_SPEEDSEL | MDIO_CTRL1_FULLDPLX);
if (ecmd->speed == SPEED_100)
reg |= MDIO_PMA_CTRL1_SPEED100;
if (ecmd->duplex)
reg |= MDIO_CTRL1_FULLDPLX;
efx_mdio_write(efx, MDIO_MMD_PMAPMD, MDIO_CTRL1, reg);
}
return 0;
}
enum efx_fc_type efx_mdio_get_pause(struct efx_nic *efx)
{
int lpa;
if (!(efx->phy_op->mmds & MDIO_DEVS_AN))
return efx->wanted_fc;
lpa = efx_mdio_read(efx, MDIO_MMD_AN, MDIO_AN_LPA);
return efx_fc_resolve(efx->wanted_fc, lpa);
}

View File

@@ -0,0 +1,107 @@
/****************************************************************************
* 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
#include <linux/mdio.h>
/*
* Helper functions for doing 10G MDIO as specified in IEEE 802.3 clause 45.
*/
#include "efx.h"
#include "boards.h"
static inline unsigned efx_mdio_id_rev(u32 id) { return id & 0xf; }
static inline unsigned efx_mdio_id_model(u32 id) { return (id >> 4) & 0x3f; }
extern unsigned efx_mdio_id_oui(u32 id);
static inline int efx_mdio_read(struct efx_nic *efx, int devad, int addr)
{
return efx->mdio.mdio_read(efx->net_dev, efx->mdio.prtad, devad, addr);
}
static inline void
efx_mdio_write(struct efx_nic *efx, int devad, int addr, int value)
{
efx->mdio.mdio_write(efx->net_dev, efx->mdio.prtad, devad, addr, value);
}
static inline u32 efx_mdio_read_id(struct efx_nic *efx, int mmd)
{
u16 id_low = efx_mdio_read(efx, mmd, MDIO_DEVID2);
u16 id_hi = efx_mdio_read(efx, mmd, MDIO_DEVID1);
return (id_hi << 16) | (id_low);
}
static inline bool efx_mdio_phyxgxs_lane_sync(struct efx_nic *efx)
{
int i, lane_status;
bool sync;
for (i = 0; i < 2; ++i)
lane_status = efx_mdio_read(efx, MDIO_MMD_PHYXS,
MDIO_PHYXS_LNSTAT);
sync = !!(lane_status & MDIO_PHYXS_LNSTAT_ALIGN);
if (!sync)
EFX_LOG(efx, "XGXS lane status: %x\n", lane_status);
return sync;
}
extern const char *efx_mdio_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 efx_mdio_reset_mmd(struct efx_nic *efx, int mmd,
int spins, int spintime);
/* As efx_mdio_check_mmd but for multiple MMDs */
int efx_mdio_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 bool efx_mdio_links_ok(struct efx_nic *efx, unsigned int mmd_mask);
/* Generic transmit disable support though PMAPMD */
extern void efx_mdio_transmit_disable(struct efx_nic *efx);
/* Generic part of reconfigure: set/clear loopback bits */
extern void efx_mdio_phy_reconfigure(struct efx_nic *efx);
/* Set the power state of the specified MMDs */
extern void efx_mdio_set_mmds_lpower(struct efx_nic *efx,
int low_power, unsigned int mmd_mask);
/* Set (some of) the PHY settings over MDIO */
extern int efx_mdio_set_settings(struct efx_nic *efx, struct ethtool_cmd *ecmd);
/* Get pause parameters from AN if available (otherwise return
* requested pause parameters)
*/
enum efx_fc_type efx_mdio_get_pause(struct efx_nic *efx);
/* Wait for specified MMDs to exit reset within a timeout */
extern int efx_mdio_wait_reset_mmds(struct efx_nic *efx,
unsigned int mmd_mask);
/* Set or clear flag, debouncing */
static inline void
efx_mdio_set_flag(struct efx_nic *efx, int devad, int addr,
int mask, bool state)
{
mdio_set_flag(&efx->mdio, efx->mdio.prtad, devad, addr, mask, state);
}
#endif /* EFX_MDIO_10G_H */

View File

@@ -0,0 +1,269 @@
/****************************************************************************
* 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/module.h>
#include <linux/mtd/mtd.h>
#include <linux/delay.h>
#define EFX_DRIVER_NAME "sfc_mtd"
#include "net_driver.h"
#include "spi.h"
#include "efx.h"
#define EFX_SPI_VERIFY_BUF_LEN 16
struct efx_mtd {
const struct efx_spi_device *spi;
struct mtd_info mtd;
char name[IFNAMSIZ + 20];
};
/* SPI utilities */
static int efx_spi_slow_wait(struct efx_mtd *efx_mtd, bool uninterruptible)
{
const struct efx_spi_device *spi = efx_mtd->spi;
struct efx_nic *efx = spi->efx;
u8 status;
int rc, i;
/* Wait up to 4s for flash/EEPROM to finish a slow operation. */
for (i = 0; i < 40; i++) {
__set_current_state(uninterruptible ?
TASK_UNINTERRUPTIBLE : TASK_INTERRUPTIBLE);
schedule_timeout(HZ / 10);
rc = falcon_spi_cmd(spi, SPI_RDSR, -1, NULL,
&status, sizeof(status));
if (rc)
return rc;
if (!(status & SPI_STATUS_NRDY))
return 0;
if (signal_pending(current))
return -EINTR;
}
EFX_ERR(efx, "timed out waiting for %s\n", efx_mtd->name);
return -ETIMEDOUT;
}
static int efx_spi_unlock(const struct efx_spi_device *spi)
{
const u8 unlock_mask = (SPI_STATUS_BP2 | SPI_STATUS_BP1 |
SPI_STATUS_BP0);
u8 status;
int rc;
rc = falcon_spi_cmd(spi, SPI_RDSR, -1, NULL, &status, sizeof(status));
if (rc)
return rc;
if (!(status & unlock_mask))
return 0; /* already unlocked */
rc = falcon_spi_cmd(spi, SPI_WREN, -1, NULL, NULL, 0);
if (rc)
return rc;
rc = falcon_spi_cmd(spi, SPI_SST_EWSR, -1, NULL, NULL, 0);
if (rc)
return rc;
status &= ~unlock_mask;
rc = falcon_spi_cmd(spi, SPI_WRSR, -1, &status, NULL, sizeof(status));
if (rc)
return rc;
rc = falcon_spi_wait_write(spi);
if (rc)
return rc;
return 0;
}
static int efx_spi_erase(struct efx_mtd *efx_mtd, loff_t start, size_t len)
{
const struct efx_spi_device *spi = efx_mtd->spi;
unsigned pos, block_len;
u8 empty[EFX_SPI_VERIFY_BUF_LEN];
u8 buffer[EFX_SPI_VERIFY_BUF_LEN];
int rc;
if (len != spi->erase_size)
return -EINVAL;
if (spi->erase_command == 0)
return -EOPNOTSUPP;
rc = efx_spi_unlock(spi);
if (rc)
return rc;
rc = falcon_spi_cmd(spi, SPI_WREN, -1, NULL, NULL, 0);
if (rc)
return rc;
rc = falcon_spi_cmd(spi, spi->erase_command, start, NULL, NULL, 0);
if (rc)
return rc;
rc = efx_spi_slow_wait(efx_mtd, false);
/* Verify the entire region has been wiped */
memset(empty, 0xff, sizeof(empty));
for (pos = 0; pos < len; pos += block_len) {
block_len = min(len - pos, sizeof(buffer));
rc = falcon_spi_read(spi, start + pos, block_len, NULL, buffer);
if (rc)
return rc;
if (memcmp(empty, buffer, block_len))
return -EIO;
/* Avoid locking up the system */
cond_resched();
if (signal_pending(current))
return -EINTR;
}
return rc;
}
/* MTD interface */
static int efx_mtd_read(struct mtd_info *mtd, loff_t start, size_t len,
size_t *retlen, u8 *buffer)
{
struct efx_mtd *efx_mtd = mtd->priv;
const struct efx_spi_device *spi = efx_mtd->spi;
struct efx_nic *efx = spi->efx;
int rc;
rc = mutex_lock_interruptible(&efx->spi_lock);
if (rc)
return rc;
rc = falcon_spi_read(spi, FALCON_FLASH_BOOTCODE_START + start,
len, retlen, buffer);
mutex_unlock(&efx->spi_lock);
return rc;
}
static int efx_mtd_erase(struct mtd_info *mtd, struct erase_info *erase)
{
struct efx_mtd *efx_mtd = mtd->priv;
struct efx_nic *efx = efx_mtd->spi->efx;
int rc;
rc = mutex_lock_interruptible(&efx->spi_lock);
if (rc)
return rc;
rc = efx_spi_erase(efx_mtd, FALCON_FLASH_BOOTCODE_START + erase->addr,
erase->len);
mutex_unlock(&efx->spi_lock);
if (rc == 0) {
erase->state = MTD_ERASE_DONE;
} else {
erase->state = MTD_ERASE_FAILED;
erase->fail_addr = 0xffffffff;
}
mtd_erase_callback(erase);
return rc;
}
static int efx_mtd_write(struct mtd_info *mtd, loff_t start,
size_t len, size_t *retlen, const u8 *buffer)
{
struct efx_mtd *efx_mtd = mtd->priv;
const struct efx_spi_device *spi = efx_mtd->spi;
struct efx_nic *efx = spi->efx;
int rc;
rc = mutex_lock_interruptible(&efx->spi_lock);
if (rc)
return rc;
rc = falcon_spi_write(spi, FALCON_FLASH_BOOTCODE_START + start,
len, retlen, buffer);
mutex_unlock(&efx->spi_lock);
return rc;
}
static void efx_mtd_sync(struct mtd_info *mtd)
{
struct efx_mtd *efx_mtd = mtd->priv;
struct efx_nic *efx = efx_mtd->spi->efx;
int rc;
mutex_lock(&efx->spi_lock);
rc = efx_spi_slow_wait(efx_mtd, true);
mutex_unlock(&efx->spi_lock);
if (rc)
EFX_ERR(efx, "%s sync failed (%d)\n", efx_mtd->name, rc);
return;
}
void efx_mtd_remove(struct efx_nic *efx)
{
if (efx->spi_flash && efx->spi_flash->mtd) {
struct efx_mtd *efx_mtd = efx->spi_flash->mtd;
int rc;
for (;;) {
rc = del_mtd_device(&efx_mtd->mtd);
if (rc != -EBUSY)
break;
ssleep(1);
}
WARN_ON(rc);
kfree(efx_mtd);
}
}
void efx_mtd_rename(struct efx_nic *efx)
{
if (efx->spi_flash && efx->spi_flash->mtd) {
struct efx_mtd *efx_mtd = efx->spi_flash->mtd;
snprintf(efx_mtd->name, sizeof(efx_mtd->name),
"%s sfc_flash_bootrom", efx->name);
}
}
int efx_mtd_probe(struct efx_nic *efx)
{
struct efx_spi_device *spi = efx->spi_flash;
struct efx_mtd *efx_mtd;
if (!spi || spi->size <= FALCON_FLASH_BOOTCODE_START)
return -ENODEV;
efx_mtd = kzalloc(sizeof(*efx_mtd), GFP_KERNEL);
if (!efx_mtd)
return -ENOMEM;
efx_mtd->spi = spi;
spi->mtd = efx_mtd;
efx_mtd->mtd.type = MTD_NORFLASH;
efx_mtd->mtd.flags = MTD_CAP_NORFLASH;
efx_mtd->mtd.size = spi->size - FALCON_FLASH_BOOTCODE_START;
efx_mtd->mtd.erasesize = spi->erase_size;
efx_mtd->mtd.writesize = 1;
efx_mtd_rename(efx);
efx_mtd->mtd.owner = THIS_MODULE;
efx_mtd->mtd.priv = efx_mtd;
efx_mtd->mtd.name = efx_mtd->name;
efx_mtd->mtd.erase = efx_mtd_erase;
efx_mtd->mtd.read = efx_mtd_read;
efx_mtd->mtd.write = efx_mtd_write;
efx_mtd->mtd.sync = efx_mtd_sync;
if (add_mtd_device(&efx_mtd->mtd)) {
kfree(efx_mtd);
spi->mtd = NULL;
/* add_mtd_device() returns 1 if the MTD table is full */
return -ENOMEM;
}
return 0;
}

View File

@@ -0,0 +1,997 @@
/****************************************************************************
* 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/mdio.h>
#include <linux/list.h>
#include <linux/pci.h>
#include <linux/device.h>
#include <linux/highmem.h>
#include <linux/workqueue.h>
#include <linux/i2c.h>
#include "enum.h"
#include "bitfield.h"
/**************************************************************************
*
* Build definitions
*
**************************************************************************/
#ifndef EFX_DRIVER_NAME
#define EFX_DRIVER_NAME "sfc"
#endif
#define EFX_DRIVER_VERSION "2.3"
#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
/* Un-rate-limited logging */
#define EFX_ERR(efx, fmt, args...) \
dev_err(&((efx)->pci_dev->dev), "ERR: %s " fmt, efx_dev_name(efx), ##args)
#define EFX_INFO(efx, fmt, args...) \
dev_info(&((efx)->pci_dev->dev), "INFO: %s " fmt, efx_dev_name(efx), ##args)
#ifdef EFX_ENABLE_DEBUG
#define EFX_LOG(efx, fmt, args...) \
dev_info(&((efx)->pci_dev->dev), "DBG: %s " fmt, efx_dev_name(efx), ##args)
#else
#define EFX_LOG(efx, fmt, args...) \
dev_dbg(&((efx)->pci_dev->dev), "DBG: %s " fmt, efx_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)
/**************************************************************************
*
* Efx data structures
*
**************************************************************************/
#define EFX_MAX_CHANNELS 32
#define EFX_MAX_RX_QUEUES EFX_MAX_CHANNELS
#define EFX_TX_QUEUE_OFFLOAD_CSUM 0
#define EFX_TX_QUEUE_NO_CSUM 1
#define EFX_TX_QUEUE_COUNT 2
/**
* 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.
* @tsoh: The associated TSO header structure, or %NULL if this
* buffer is not a TSO header.
* @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_len: Length of this fragment to unmap
*/
struct efx_tx_buffer {
const struct sk_buff *skb;
struct efx_tso_header *tsoh;
dma_addr_t dma_addr;
unsigned short len;
bool continuation;
bool unmap_single;
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
* @channel: The associated channel
* @buffer: The software buffer ring
* @txd: The hardware descriptor ring
* @flushed: Used when handling queue flushing
* @read_count: Current read pointer.
* This is the number of buffers that have been removed from both rings.
* @stopped: Stopped count.
* 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.
* @tso_headers_free: A list of TSO headers allocated for this TX queue
* that are not in use, and so available for new TSO sends. The list
* is protected by the TX queue lock.
* @tso_bursts: Number of times TSO xmit invoked by kernel
* @tso_long_headers: Number of packets with headers too long for standard
* blocks
* @tso_packets: Number of packets via the TSO xmit path
*/
struct efx_tx_queue {
/* Members which don't change on the fast path */
struct efx_nic *efx ____cacheline_aligned_in_smp;
int queue;
struct efx_channel *channel;
struct efx_nic *nic;
struct efx_tx_buffer *buffer;
struct efx_special_buffer txd;
bool flushed;
/* 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_tso_header *tso_headers_free;
unsigned int tso_bursts;
unsigned int tso_long_headers;
unsigned int tso_packets;
};
/**
* 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
* @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.
* @flushed: Use when handling queue flushing
*/
struct efx_rx_queue {
struct efx_nic *efx;
int queue;
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;
bool flushed;
};
/**
* 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
* @channel: Channel instance number
* @name: Name for channel and IRQ
* @used_flags: Channel is used by net driver
* @enabled: Channel enabled indicator
* @irq: IRQ number (MSI and MSI-X only)
* @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
* @irq_count: Number of IRQs since last adaptive moderation decision
* @irq_mod_score: IRQ moderation score
* @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
* @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 channel;
char name[IFNAMSIZ + 6];
int used_flags;
bool enabled;
int irq;
unsigned int irq_moderation;
struct net_device *napi_dev;
struct napi_struct napi_str;
bool work_pending;
struct efx_special_buffer eventq;
unsigned int eventq_read_ptr;
unsigned int last_eventq_read_ptr;
unsigned int eventq_magic;
unsigned int irq_count;
unsigned int irq_mod_score;
int rx_alloc_level;
int rx_alloc_push_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;
bool rx_pkt_csummed;
};
/**
* struct efx_blinker - S/W LED blinking context
* @state: Current state - on or off
* @resubmit: Timer resubmission flag
* @timer: Control timer for blinking
*/
struct efx_blinker {
bool state;
bool 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. May be called repeatedly.
* @set_id_led: Turns the identification LED on or off
* @blink: Starts/stops blinking
* @monitor: Board-specific health check function
* @fini: Cleanup function
* @blinker: used to blink LEDs in software
* @hwmon_client: I2C client for hardware monitor
* @ioexp_client: I2C client for power/port control
*/
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. */
void (*init_leds)(struct efx_nic *efx);
void (*set_id_led) (struct efx_nic *efx, bool state);
int (*monitor) (struct efx_nic *nic);
void (*blink) (struct efx_nic *efx, bool start);
void (*fini) (struct efx_nic *nic);
struct efx_blinker blinker;
struct i2c_client *hwmon_client, *ioexp_client;
};
#define STRING_TABLE_LOOKUP(val, member) \
member ## _names[val]
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_TXC43128 = 1,
PHY_TYPE_88E1111 = 2,
PHY_TYPE_SFX7101 = 3,
PHY_TYPE_QT2022C2 = 4,
PHY_TYPE_PM8358 = 6,
PHY_TYPE_SFT9001A = 8,
PHY_TYPE_QT2025C = 9,
PHY_TYPE_SFT9001B = 10,
PHY_TYPE_MAX /* Insert any new items before this */
};
#define EFX_IS10G(efx) ((efx)->link_speed == 10000)
enum nic_state {
STATE_INIT = 0,
STATE_RUNNING = 1,
STATE_FINI = 2,
STATE_DISABLED = 3,
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].
*/
#ifdef CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS
#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 = FLOW_CTRL_RX,
EFX_FC_TX = FLOW_CTRL_TX,
EFX_FC_AUTO = 4,
};
/* Supported MAC bit-mask */
enum efx_mac_type {
EFX_GMAC = 1,
EFX_XMAC = 2,
};
static inline enum efx_fc_type efx_fc_resolve(enum efx_fc_type wanted_fc,
unsigned int lpa)
{
BUILD_BUG_ON(EFX_FC_AUTO & (EFX_FC_RX | EFX_FC_TX));
if (!(wanted_fc & EFX_FC_AUTO))
return wanted_fc;
return mii_resolve_flowctrl_fdx(mii_advertise_flowctrl(wanted_fc), lpa);
}
/**
* struct efx_mac_operations - Efx MAC operations table
* @reconfigure: Reconfigure MAC. Serialised by the mac_lock
* @update_stats: Update statistics
* @irq: Hardware MAC event callback. Serialised by the mac_lock
* @poll: Poll for hardware state. Serialised by the mac_lock
*/
struct efx_mac_operations {
void (*reconfigure) (struct efx_nic *efx);
void (*update_stats) (struct efx_nic *efx);
void (*irq) (struct efx_nic *efx);
void (*poll) (struct efx_nic *efx);
};
/**
* 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
* @poll: Poll for hardware state. Serialised by the mac_lock.
* @get_settings: Get ethtool settings. Serialised by the mac_lock.
* @set_settings: Set ethtool settings. Serialised by the mac_lock.
* @set_npage_adv: Set abilities advertised in (Extended) Next Page
* (only needed where AN bit is set in mmds)
* @num_tests: Number of PHY-specific tests/results
* @test_names: Names of the tests/results
* @run_tests: Run tests and record results as appropriate.
* Flags are the ethtool tests flags.
* @mmds: MMD presence mask
* @loopbacks: Supported loopback modes mask
*/
struct efx_phy_operations {
enum efx_mac_type macs;
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);
void (*poll) (struct efx_nic *efx);
void (*get_settings) (struct efx_nic *efx,
struct ethtool_cmd *ecmd);
int (*set_settings) (struct efx_nic *efx,
struct ethtool_cmd *ecmd);
void (*set_npage_adv) (struct efx_nic *efx, u32);
u32 num_tests;
const char *const *test_names;
int (*run_tests) (struct efx_nic *efx, int *results, unsigned flags);
int mmds;
unsigned loopbacks;
};
/**
* @enum efx_phy_mode - PHY operating mode flags
* @PHY_MODE_NORMAL: on and should pass traffic
* @PHY_MODE_TX_DISABLED: on with TX disabled
* @PHY_MODE_LOW_POWER: set to low power through MDIO
* @PHY_MODE_OFF: switched off through external control
* @PHY_MODE_SPECIAL: on but will not pass traffic
*/
enum efx_phy_mode {
PHY_MODE_NORMAL = 0,
PHY_MODE_TX_DISABLED = 1,
PHY_MODE_LOW_POWER = 2,
PHY_MODE_OFF = 4,
PHY_MODE_SPECIAL = 8,
};
static inline bool efx_phy_mode_disabled(enum efx_phy_mode mode)
{
return !!(mode & ~PHY_MODE_TX_DISABLED);
}
/*
* 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 port reconfigures and the HW monitor.
* Work items do not hold and must not acquire RTNL.
* @workqueue_name: Name of workqueue
* @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
* @irq_rx_adaptive: Adaptive IRQ moderation enabled for RX event queues
* @irq_rx_moderation: IRQ moderation time for RX event queues
* @i2c_adap: I2C adapter
* @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
* @n_rx_queues: Number of RX queues
* @n_channels: Number of channels in use
* @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.
* @spi_flash: SPI flash device
* This field will be %NULL if no flash device is present.
* @spi_eeprom: SPI EEPROM device
* This field will be %NULL if no EEPROM device is present.
* @spi_lock: SPI bus lock
* @n_rx_nodesc_drop_cnt: RX no descriptor drop count
* @nic_data: Hardware dependant state
* @mac_lock: MAC access lock. Protects @port_enabled, @phy_mode,
* @port_inhibited, efx_monitor() and efx_reconfigure_port()
* @port_enabled: Port enabled indicator.
* Serialises efx_stop_all(), efx_start_all(), efx_monitor(),
* efx_phy_work(), and efx_mac_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_inhibited: If set, the netif_carrier is always off. Hold the mac_lock
* @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. Serialises statistics fetches
* @stats_disable_count: Nest count for disabling statistics fetches
* @mac_op: MAC interface
* @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)
* @mdio: PHY MDIO interface
* @phy_mode: PHY operating mode. Serialised by @mac_lock.
* @mac_up: MAC link state
* @link_up: Link status
* @link_fd: Link is full duplex
* @link_fc: Actualy flow control flags
* @link_speed: Link speed (Mbps)
* @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
* @wanted_fc: Wanted flow control flags
* @phy_work: work item for dealing with PHY events
* @mac_work: work item for dealing with MAC events
* @loopback_mode: Loopback status
* @loopback_modes: Supported loopback mode bitmask
* @loopback_selftest: Offline self-test private state
*
* 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;
char workqueue_name[16];
struct work_struct reset_work;
struct delayed_work monitor_work;
resource_size_t membase_phys;
void __iomem *membase;
spinlock_t biu_lock;
enum efx_int_mode interrupt_mode;
bool irq_rx_adaptive;
unsigned int irq_rx_moderation;
struct i2c_adapter i2c_adap;
struct efx_board board_info;
enum nic_state state;
enum reset_type reset_pending;
struct efx_tx_queue tx_queue[EFX_TX_QUEUE_COUNT];
struct efx_rx_queue rx_queue[EFX_MAX_RX_QUEUES];
struct efx_channel channel[EFX_MAX_CHANNELS];
int n_rx_queues;
int n_channels;
unsigned int rx_buffer_len;
unsigned int rx_buffer_order;
struct efx_buffer irq_status;
volatile signed int last_irq_cpu;
struct efx_spi_device *spi_flash;
struct efx_spi_device *spi_eeprom;
struct mutex spi_lock;
unsigned n_rx_nodesc_drop_cnt;
struct falcon_nic_data *nic_data;
struct mutex mac_lock;
struct work_struct mac_work;
bool port_enabled;
bool port_inhibited;
bool port_initialized;
struct net_device *net_dev;
bool 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 int stats_disable_count;
struct efx_mac_operations *mac_op;
unsigned char mac_address[ETH_ALEN];
enum phy_type phy_type;
spinlock_t phy_lock;
struct work_struct phy_work;
struct efx_phy_operations *phy_op;
void *phy_data;
struct mdio_if_info mdio;
enum efx_phy_mode phy_mode;
bool mac_up;
bool link_up;
bool link_fd;
enum efx_fc_type link_fc;
unsigned int link_speed;
unsigned int n_link_state_changes;
bool promiscuous;
union efx_multicast_hash multicast_hash;
enum efx_fc_type wanted_fc;
atomic_t rx_reset;
enum efx_loopback_mode loopback_mode;
unsigned int loopback_modes;
void *loopback_selftest;
};
static inline int efx_dev_registered(struct efx_nic *efx)
{
return efx->net_dev->reg_state == NETREG_REGISTERED;
}
/* Net device name, for inclusion in log messages if it has been registered.
* Use efx->name not efx->net_dev->name so that races with (un)registration
* are harmless.
*/
static inline const char *efx_dev_name(struct efx_nic *efx)
{
return efx_dev_registered(efx) ? efx->name : "";
}
/**
* 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;
u64 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 TX queues */
#define efx_for_each_tx_queue(_tx_queue, _efx) \
for (_tx_queue = &_efx->tx_queue[0]; \
_tx_queue < &_efx->tx_queue[EFX_TX_QUEUE_COUNT]; \
_tx_queue++)
/* 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_TX_QUEUE_COUNT]; \
_tx_queue++) \
if (_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->n_rx_queues]; \
_rx_queue++)
/* 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[_channel->channel]; \
_rx_queue; \
_rx_queue = NULL) \
if (_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(unsigned nr, unsigned char *addr)
{
addr[nr / 8] |= (1 << (nr % 8));
}
/* Clear bit in a little-endian bitfield */
static inline void clear_bit_le(unsigned 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.
*
* Re-clocking by the XGXS on RX can reduce an IPG to 32 bits (half an
* XGMII cycle). If the frame length reaches the maximum value in the
* same cycle, the XMAC can miss the IPG altogether. We work around
* this by adding a further 16 bytes.
*/
#define EFX_MAX_FRAME_LEN(mtu) \
((((mtu) + ETH_HLEN + VLAN_HLEN + 4/* FCS */ + 7) & ~7) + 16)
#endif /* EFX_NET_DRIVER_H */

View File

@@ -0,0 +1,44 @@
/****************************************************************************
* Driver for Solarflare Solarstorm network controllers and boards
* Copyright 2007-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_PHY_H
#define EFX_PHY_H
/****************************************************************************
* 10Xpress (SFX7101 and SFT9001) PHYs
*/
extern struct efx_phy_operations falcon_sfx7101_phy_ops;
extern struct efx_phy_operations falcon_sft9001_phy_ops;
extern void tenxpress_phy_blink(struct efx_nic *efx, bool blink);
/* Wait for the PHY to boot. Return 0 on success, -EINVAL if the PHY failed
* to boot due to corrupt flash, or some other negative error code. */
extern int sft9001_wait_boot(struct efx_nic *efx);
/****************************************************************************
* AMCC/Quake QT20xx PHYs
*/
extern struct efx_phy_operations falcon_xfp_phy_ops;
/* These PHYs provide 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

720
kernel/drivers/net/sfc/rx.c Normal file
View File

@@ -0,0 +1,720 @@
/****************************************************************************
* 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 "selftest.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
static inline unsigned int efx_rx_buf_offset(struct efx_rx_buffer *buf)
{
/* Offset is always within one page, so we don't need to consider
* the page order.
*/
return (__force unsigned long) buf->data & (PAGE_SIZE - 1);
}
static inline unsigned int efx_rx_buf_size(struct efx_nic *efx)
{
return PAGE_SIZE << efx->rx_buffer_order;
}
/**
* 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 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(efx->pci_dev, 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 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, efx_rx_buf_size(efx),
PCI_DMA_FROMDEVICE);
if (unlikely(pci_dma_mapping_error(efx->pci_dev, 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 = (page_address(rx_buf->page) +
EFX_PAGE_IP_ALIGN);
}
rx_buf->len = bytes;
rx_buf->data = rx_queue->buf_data;
offset = efx_rx_buf_offset(rx_buf);
rx_buf->dma_addr = rx_queue->buf_dma_addr + offset;
/* 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 = efx_rx_buf_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 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 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,
efx_rx_buf_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 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 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 void efx_rx_packet__check_len(struct efx_rx_queue *rx_queue,
struct efx_rx_buffer *rx_buf,
int len, bool *discard,
bool *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 = true;
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 void efx_rx_packet_lro(struct efx_channel *channel,
struct efx_rx_buffer *rx_buf,
bool checksummed)
{
struct napi_struct *napi = &channel->napi_str;
/* Pass the skb/page into the LRO engine */
if (rx_buf->page) {
struct sk_buff *skb = napi_get_frags(napi);
if (!skb) {
put_page(rx_buf->page);
goto out;
}
skb_shinfo(skb)->frags[0].page = rx_buf->page;
skb_shinfo(skb)->frags[0].page_offset =
efx_rx_buf_offset(rx_buf);
skb_shinfo(skb)->frags[0].size = rx_buf->len;
skb_shinfo(skb)->nr_frags = 1;
skb->len = rx_buf->len;
skb->data_len = rx_buf->len;
skb->truesize += rx_buf->len;
skb->ip_summed =
checksummed ? CHECKSUM_UNNECESSARY : CHECKSUM_NONE;
napi_gro_frags(napi);
out:
EFX_BUG_ON_PARANOID(rx_buf->skb);
rx_buf->page = NULL;
} else {
EFX_BUG_ON_PARANOID(!rx_buf->skb);
EFX_BUG_ON_PARANOID(!checksummed);
napi_gro_receive(napi, rx_buf->skb);
rx_buf->skb = NULL;
}
}
void efx_rx_packet(struct efx_rx_queue *rx_queue, unsigned int index,
unsigned int len, bool checksummed, bool discard)
{
struct efx_nic *efx = rx_queue->efx;
struct efx_rx_buffer *rx_buf;
bool leak_packet = false;
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, bool checksummed)
{
struct efx_nic *efx = channel->efx;
struct sk_buff *skb;
/* If we're in loopback test, then pass the packet directly to the
* loopback layer, and free the rx_buf here
*/
if (unlikely(efx->loopback_selftest)) {
efx_loopback_rx_packet(efx, rx_buf->data, rx_buf->len);
efx_free_rx_buffer(efx, rx_buf);
goto done;
}
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);
}
if (likely(checksummed || rx_buf->page)) {
efx_rx_packet_lro(channel, rx_buf, checksummed);
goto done;
}
/* 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 */
skb->ip_summed = CHECKSUM_NONE;
skb_record_rx_queue(skb, channel->channel);
/* Pass the packet up */
netif_receive_skb(skb);
/* Update allocation strategy method */
channel->rx_alloc_level += RX_ALLOC_FACTOR_SKB;
done:
;
}
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_GRO)) {
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)
return -ENOMEM;
rc = falcon_probe_rx(rx_queue);
if (rc) {
kfree(rx_queue->buffer);
rx_queue->buffer = NULL;
}
return rc;
}
void 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 */
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,
efx_rx_buf_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;
}
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 (%)");

View File

@@ -0,0 +1,26 @@
/****************************************************************************
* 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);
void efx_init_rx_queue(struct efx_rx_queue *rx_queue);
void efx_fini_rx_queue(struct efx_rx_queue *rx_queue);
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, bool checksummed);
#endif /* EFX_RX_H */

View File

@@ -0,0 +1,763 @@
/****************************************************************************
* 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/module.h>
#include <linux/delay.h>
#include <linux/kernel_stat.h>
#include <linux/pci.h>
#include <linux/ethtool.h>
#include <linux/ip.h>
#include <linux/in.h>
#include <linux/udp.h>
#include <linux/rtnetlink.h>
#include <asm/io.h>
#include "net_driver.h"
#include "ethtool.h"
#include "efx.h"
#include "falcon.h"
#include "selftest.h"
#include "boards.h"
#include "workarounds.h"
#include "spi.h"
#include "falcon_io.h"
#include "mdio_10g.h"
/*
* Loopback test packet structure
*
* The self-test should stress every RSS vector, and unfortunately
* Falcon only performs RSS on TCP/UDP packets.
*/
struct efx_loopback_payload {
struct ethhdr header;
struct iphdr ip;
struct udphdr udp;
__be16 iteration;
const char msg[64];
} __attribute__ ((packed));
/* Loopback test source MAC address */
static const unsigned char payload_source[ETH_ALEN] = {
0x00, 0x0f, 0x53, 0x1b, 0x1b, 0x1b,
};
static const char *payload_msg =
"Hello world! This is an Efx loopback test in progress!";
/**
* efx_loopback_state - persistent state during a loopback selftest
* @flush: Drop all packets in efx_loopback_rx_packet
* @packet_count: Number of packets being used in this test
* @skbs: An array of skbs transmitted
* @rx_good: RX good packet count
* @rx_bad: RX bad packet count
* @payload: Payload used in tests
*/
struct efx_loopback_state {
bool flush;
int packet_count;
struct sk_buff **skbs;
/* Checksums are being offloaded */
bool offload_csum;
atomic_t rx_good;
atomic_t rx_bad;
struct efx_loopback_payload payload;
};
/**************************************************************************
*
* MII, NVRAM and register tests
*
**************************************************************************/
static int efx_test_mdio(struct efx_nic *efx, struct efx_self_tests *tests)
{
int rc = 0;
int devad = __ffs(efx->mdio.mmds);
u16 physid1, physid2;
if (efx->phy_type == PHY_TYPE_NONE)
return 0;
mutex_lock(&efx->mac_lock);
tests->mdio = -1;
physid1 = efx_mdio_read(efx, devad, MDIO_DEVID1);
physid2 = efx_mdio_read(efx, devad, MDIO_DEVID2);
if ((physid1 == 0x0000) || (physid1 == 0xffff) ||
(physid2 == 0x0000) || (physid2 == 0xffff)) {
EFX_ERR(efx, "no MDIO PHY present with ID %d\n",
efx->mdio.prtad);
rc = -EINVAL;
goto out;
}
if (EFX_IS10G(efx)) {
rc = efx_mdio_check_mmds(efx, efx->phy_op->mmds, 0);
if (rc)
goto out;
}
out:
mutex_unlock(&efx->mac_lock);
tests->mdio = rc ? -1 : 1;
return rc;
}
static int efx_test_nvram(struct efx_nic *efx, struct efx_self_tests *tests)
{
int rc;
rc = falcon_read_nvram(efx, NULL);
tests->nvram = rc ? -1 : 1;
return rc;
}
static int efx_test_chip(struct efx_nic *efx, struct efx_self_tests *tests)
{
int rc;
/* Not supported on A-series silicon */
if (falcon_rev(efx) < FALCON_REV_B0)
return 0;
rc = falcon_test_registers(efx);
tests->registers = rc ? -1 : 1;
return rc;
}
/**************************************************************************
*
* Interrupt and event queue testing
*
**************************************************************************/
/* Test generation and receipt of interrupts */
static int efx_test_interrupts(struct efx_nic *efx,
struct efx_self_tests *tests)
{
struct efx_channel *channel;
EFX_LOG(efx, "testing interrupts\n");
tests->interrupt = -1;
/* Reset interrupt flag */
efx->last_irq_cpu = -1;
smp_wmb();
/* ACK each interrupting event queue. Receiving an interrupt due to
* traffic before a test event is raised is considered a pass */
efx_for_each_channel(channel, efx) {
if (channel->work_pending)
efx_process_channel_now(channel);
if (efx->last_irq_cpu >= 0)
goto success;
}
falcon_generate_interrupt(efx);
/* Wait for arrival of test interrupt. */
EFX_LOG(efx, "waiting for test interrupt\n");
schedule_timeout_uninterruptible(HZ / 10);
if (efx->last_irq_cpu >= 0)
goto success;
EFX_ERR(efx, "timed out waiting for interrupt\n");
return -ETIMEDOUT;
success:
EFX_LOG(efx, "test interrupt (mode %d) seen on CPU%d\n",
efx->interrupt_mode, efx->last_irq_cpu);
tests->interrupt = 1;
return 0;
}
/* Test generation and receipt of interrupting events */
static int efx_test_eventq_irq(struct efx_channel *channel,
struct efx_self_tests *tests)
{
unsigned int magic, count;
/* Channel specific code, limited to 20 bits */
magic = (0x00010150 + channel->channel);
EFX_LOG(channel->efx, "channel %d testing event queue with code %x\n",
channel->channel, magic);
tests->eventq_dma[channel->channel] = -1;
tests->eventq_int[channel->channel] = -1;
tests->eventq_poll[channel->channel] = -1;
/* Reset flag and zero magic word */
channel->efx->last_irq_cpu = -1;
channel->eventq_magic = 0;
smp_wmb();
falcon_generate_test_event(channel, magic);
/* Wait for arrival of interrupt */
count = 0;
do {
schedule_timeout_uninterruptible(HZ / 100);
if (channel->work_pending)
efx_process_channel_now(channel);
if (channel->eventq_magic == magic)
goto eventq_ok;
} while (++count < 2);
EFX_ERR(channel->efx, "channel %d timed out waiting for event queue\n",
channel->channel);
/* See if interrupt arrived */
if (channel->efx->last_irq_cpu >= 0) {
EFX_ERR(channel->efx, "channel %d saw interrupt on CPU%d "
"during event queue test\n", channel->channel,
raw_smp_processor_id());
tests->eventq_int[channel->channel] = 1;
}
/* Check to see if event was received even if interrupt wasn't */
efx_process_channel_now(channel);
if (channel->eventq_magic == magic) {
EFX_ERR(channel->efx, "channel %d event was generated, but "
"failed to trigger an interrupt\n", channel->channel);
tests->eventq_dma[channel->channel] = 1;
}
return -ETIMEDOUT;
eventq_ok:
EFX_LOG(channel->efx, "channel %d event queue passed\n",
channel->channel);
tests->eventq_dma[channel->channel] = 1;
tests->eventq_int[channel->channel] = 1;
tests->eventq_poll[channel->channel] = 1;
return 0;
}
static int efx_test_phy(struct efx_nic *efx, struct efx_self_tests *tests,
unsigned flags)
{
int rc;
if (!efx->phy_op->run_tests)
return 0;
EFX_BUG_ON_PARANOID(efx->phy_op->num_tests == 0 ||
efx->phy_op->num_tests > EFX_MAX_PHY_TESTS);
mutex_lock(&efx->mac_lock);
rc = efx->phy_op->run_tests(efx, tests->phy, flags);
mutex_unlock(&efx->mac_lock);
return rc;
}
/**************************************************************************
*
* Loopback testing
* NB Only one loopback test can be executing concurrently.
*
**************************************************************************/
/* Loopback test RX callback
* This is called for each received packet during loopback testing.
*/
void efx_loopback_rx_packet(struct efx_nic *efx,
const char *buf_ptr, int pkt_len)
{
struct efx_loopback_state *state = efx->loopback_selftest;
struct efx_loopback_payload *received;
struct efx_loopback_payload *payload;
BUG_ON(!buf_ptr);
/* If we are just flushing, then drop the packet */
if ((state == NULL) || state->flush)
return;
payload = &state->payload;
received = (struct efx_loopback_payload *) buf_ptr;
received->ip.saddr = payload->ip.saddr;
if (state->offload_csum)
received->ip.check = payload->ip.check;
/* Check that header exists */
if (pkt_len < sizeof(received->header)) {
EFX_ERR(efx, "saw runt RX packet (length %d) in %s loopback "
"test\n", pkt_len, LOOPBACK_MODE(efx));
goto err;
}
/* Check that the ethernet header exists */
if (memcmp(&received->header, &payload->header, ETH_HLEN) != 0) {
EFX_ERR(efx, "saw non-loopback RX packet in %s loopback test\n",
LOOPBACK_MODE(efx));
goto err;
}
/* Check packet length */
if (pkt_len != sizeof(*payload)) {
EFX_ERR(efx, "saw incorrect RX packet length %d (wanted %d) in "
"%s loopback test\n", pkt_len, (int)sizeof(*payload),
LOOPBACK_MODE(efx));
goto err;
}
/* Check that IP header matches */
if (memcmp(&received->ip, &payload->ip, sizeof(payload->ip)) != 0) {
EFX_ERR(efx, "saw corrupted IP header in %s loopback test\n",
LOOPBACK_MODE(efx));
goto err;
}
/* Check that msg and padding matches */
if (memcmp(&received->msg, &payload->msg, sizeof(received->msg)) != 0) {
EFX_ERR(efx, "saw corrupted RX packet in %s loopback test\n",
LOOPBACK_MODE(efx));
goto err;
}
/* Check that iteration matches */
if (received->iteration != payload->iteration) {
EFX_ERR(efx, "saw RX packet from iteration %d (wanted %d) in "
"%s loopback test\n", ntohs(received->iteration),
ntohs(payload->iteration), LOOPBACK_MODE(efx));
goto err;
}
/* Increase correct RX count */
EFX_TRACE(efx, "got loopback RX in %s loopback test\n",
LOOPBACK_MODE(efx));
atomic_inc(&state->rx_good);
return;
err:
#ifdef EFX_ENABLE_DEBUG
if (atomic_read(&state->rx_bad) == 0) {
EFX_ERR(efx, "received packet:\n");
print_hex_dump(KERN_ERR, "", DUMP_PREFIX_OFFSET, 0x10, 1,
buf_ptr, pkt_len, 0);
EFX_ERR(efx, "expected packet:\n");
print_hex_dump(KERN_ERR, "", DUMP_PREFIX_OFFSET, 0x10, 1,
&state->payload, sizeof(state->payload), 0);
}
#endif
atomic_inc(&state->rx_bad);
}
/* Initialise an efx_selftest_state for a new iteration */
static void efx_iterate_state(struct efx_nic *efx)
{
struct efx_loopback_state *state = efx->loopback_selftest;
struct net_device *net_dev = efx->net_dev;
struct efx_loopback_payload *payload = &state->payload;
/* Initialise the layerII header */
memcpy(&payload->header.h_dest, net_dev->dev_addr, ETH_ALEN);
memcpy(&payload->header.h_source, &payload_source, ETH_ALEN);
payload->header.h_proto = htons(ETH_P_IP);
/* saddr set later and used as incrementing count */
payload->ip.daddr = htonl(INADDR_LOOPBACK);
payload->ip.ihl = 5;
payload->ip.check = htons(0xdead);
payload->ip.tot_len = htons(sizeof(*payload) - sizeof(struct ethhdr));
payload->ip.version = IPVERSION;
payload->ip.protocol = IPPROTO_UDP;
/* Initialise udp header */
payload->udp.source = 0;
payload->udp.len = htons(sizeof(*payload) - sizeof(struct ethhdr) -
sizeof(struct iphdr));
payload->udp.check = 0; /* checksum ignored */
/* Fill out payload */
payload->iteration = htons(ntohs(payload->iteration) + 1);
memcpy(&payload->msg, payload_msg, sizeof(payload_msg));
/* Fill out remaining state members */
atomic_set(&state->rx_good, 0);
atomic_set(&state->rx_bad, 0);
smp_wmb();
}
static int efx_begin_loopback(struct efx_tx_queue *tx_queue)
{
struct efx_nic *efx = tx_queue->efx;
struct efx_loopback_state *state = efx->loopback_selftest;
struct efx_loopback_payload *payload;
struct sk_buff *skb;
int i;
netdev_tx_t rc;
/* Transmit N copies of buffer */
for (i = 0; i < state->packet_count; i++) {
/* Allocate an skb, holding an extra reference for
* transmit completion counting */
skb = alloc_skb(sizeof(state->payload), GFP_KERNEL);
if (!skb)
return -ENOMEM;
state->skbs[i] = skb;
skb_get(skb);
/* Copy the payload in, incrementing the source address to
* exercise the rss vectors */
payload = ((struct efx_loopback_payload *)
skb_put(skb, sizeof(state->payload)));
memcpy(payload, &state->payload, sizeof(state->payload));
payload->ip.saddr = htonl(INADDR_LOOPBACK | (i << 2));
/* Ensure everything we've written is visible to the
* interrupt handler. */
smp_wmb();
if (efx_dev_registered(efx))
netif_tx_lock_bh(efx->net_dev);
rc = efx_xmit(efx, tx_queue, skb);
if (efx_dev_registered(efx))
netif_tx_unlock_bh(efx->net_dev);
if (rc != NETDEV_TX_OK) {
EFX_ERR(efx, "TX queue %d could not transmit packet %d "
"of %d in %s loopback test\n", tx_queue->queue,
i + 1, state->packet_count, LOOPBACK_MODE(efx));
/* Defer cleaning up the other skbs for the caller */
kfree_skb(skb);
return -EPIPE;
}
efx->net_dev->trans_start = jiffies;
}
return 0;
}
static int efx_poll_loopback(struct efx_nic *efx)
{
struct efx_loopback_state *state = efx->loopback_selftest;
struct efx_channel *channel;
/* NAPI polling is not enabled, so process channels
* synchronously */
efx_for_each_channel(channel, efx) {
if (channel->work_pending)
efx_process_channel_now(channel);
}
return atomic_read(&state->rx_good) == state->packet_count;
}
static int efx_end_loopback(struct efx_tx_queue *tx_queue,
struct efx_loopback_self_tests *lb_tests)
{
struct efx_nic *efx = tx_queue->efx;
struct efx_loopback_state *state = efx->loopback_selftest;
struct sk_buff *skb;
int tx_done = 0, rx_good, rx_bad;
int i, rc = 0;
if (efx_dev_registered(efx))
netif_tx_lock_bh(efx->net_dev);
/* Count the number of tx completions, and decrement the refcnt. Any
* skbs not already completed will be free'd when the queue is flushed */
for (i=0; i < state->packet_count; i++) {
skb = state->skbs[i];
if (skb && !skb_shared(skb))
++tx_done;
dev_kfree_skb_any(skb);
}
if (efx_dev_registered(efx))
netif_tx_unlock_bh(efx->net_dev);
/* Check TX completion and received packet counts */
rx_good = atomic_read(&state->rx_good);
rx_bad = atomic_read(&state->rx_bad);
if (tx_done != state->packet_count) {
/* Don't free the skbs; they will be picked up on TX
* overflow or channel teardown.
*/
EFX_ERR(efx, "TX queue %d saw only %d out of an expected %d "
"TX completion events in %s loopback test\n",
tx_queue->queue, tx_done, state->packet_count,
LOOPBACK_MODE(efx));
rc = -ETIMEDOUT;
/* Allow to fall through so we see the RX errors as well */
}
/* We may always be up to a flush away from our desired packet total */
if (rx_good != state->packet_count) {
EFX_LOG(efx, "TX queue %d saw only %d out of an expected %d "
"received packets in %s loopback test\n",
tx_queue->queue, rx_good, state->packet_count,
LOOPBACK_MODE(efx));
rc = -ETIMEDOUT;
/* Fall through */
}
/* Update loopback test structure */
lb_tests->tx_sent[tx_queue->queue] += state->packet_count;
lb_tests->tx_done[tx_queue->queue] += tx_done;
lb_tests->rx_good += rx_good;
lb_tests->rx_bad += rx_bad;
return rc;
}
static int
efx_test_loopback(struct efx_tx_queue *tx_queue,
struct efx_loopback_self_tests *lb_tests)
{
struct efx_nic *efx = tx_queue->efx;
struct efx_loopback_state *state = efx->loopback_selftest;
int i, begin_rc, end_rc;
for (i = 0; i < 3; i++) {
/* Determine how many packets to send */
state->packet_count = (efx->type->txd_ring_mask + 1) / 3;
state->packet_count = min(1 << (i << 2), state->packet_count);
state->skbs = kzalloc(sizeof(state->skbs[0]) *
state->packet_count, GFP_KERNEL);
if (!state->skbs)
return -ENOMEM;
state->flush = false;
EFX_LOG(efx, "TX queue %d testing %s loopback with %d "
"packets\n", tx_queue->queue, LOOPBACK_MODE(efx),
state->packet_count);
efx_iterate_state(efx);
begin_rc = efx_begin_loopback(tx_queue);
/* This will normally complete very quickly, but be
* prepared to wait up to 100 ms. */
msleep(1);
if (!efx_poll_loopback(efx)) {
msleep(100);
efx_poll_loopback(efx);
}
end_rc = efx_end_loopback(tx_queue, lb_tests);
kfree(state->skbs);
if (begin_rc || end_rc) {
/* Wait a while to ensure there are no packets
* floating around after a failure. */
schedule_timeout_uninterruptible(HZ / 10);
return begin_rc ? begin_rc : end_rc;
}
}
EFX_LOG(efx, "TX queue %d passed %s loopback test with a burst length "
"of %d packets\n", tx_queue->queue, LOOPBACK_MODE(efx),
state->packet_count);
return 0;
}
static int efx_test_loopbacks(struct efx_nic *efx, struct efx_self_tests *tests,
unsigned int loopback_modes)
{
enum efx_loopback_mode mode;
struct efx_loopback_state *state;
struct efx_tx_queue *tx_queue;
bool link_up;
int count, rc = 0;
/* Set the port loopback_selftest member. From this point on
* all received packets will be dropped. Mark the state as
* "flushing" so all inflight packets are dropped */
state = kzalloc(sizeof(*state), GFP_KERNEL);
if (state == NULL)
return -ENOMEM;
BUG_ON(efx->loopback_selftest);
state->flush = true;
efx->loopback_selftest = state;
/* Test all supported loopback modes */
for (mode = LOOPBACK_NONE; mode <= LOOPBACK_TEST_MAX; mode++) {
if (!(loopback_modes & (1 << mode)))
continue;
/* Move the port into the specified loopback mode. */
state->flush = true;
efx->loopback_mode = mode;
efx_reconfigure_port(efx);
/* Wait for the PHY to signal the link is up. Interrupts
* are enabled for PHY's using LASI, otherwise we poll()
* quickly */
count = 0;
do {
struct efx_channel *channel = &efx->channel[0];
efx->phy_op->poll(efx);
schedule_timeout_uninterruptible(HZ / 10);
if (channel->work_pending)
efx_process_channel_now(channel);
/* Wait for PHY events to be processed */
flush_workqueue(efx->workqueue);
rmb();
/* We need both the phy and xaui links to be ok.
* rather than relying on the falcon_xmac irq/poll
* regime, just poll xaui directly */
link_up = efx->link_up;
if (link_up && EFX_IS10G(efx) &&
!falcon_xaui_link_ok(efx))
link_up = false;
} while ((++count < 20) && !link_up);
/* The link should now be up. If it isn't, there is no point
* in attempting a loopback test */
if (!link_up) {
EFX_ERR(efx, "loopback %s never came up\n",
LOOPBACK_MODE(efx));
rc = -EIO;
goto out;
}
EFX_LOG(efx, "link came up in %s loopback in %d iterations\n",
LOOPBACK_MODE(efx), count);
/* Test every TX queue */
efx_for_each_tx_queue(tx_queue, efx) {
state->offload_csum = (tx_queue->queue ==
EFX_TX_QUEUE_OFFLOAD_CSUM);
rc = efx_test_loopback(tx_queue,
&tests->loopback[mode]);
if (rc)
goto out;
}
}
out:
/* Remove the flush. The caller will remove the loopback setting */
state->flush = true;
efx->loopback_selftest = NULL;
wmb();
kfree(state);
return rc;
}
/**************************************************************************
*
* Entry point
*
*************************************************************************/
int efx_selftest(struct efx_nic *efx, struct efx_self_tests *tests,
unsigned flags)
{
enum efx_loopback_mode loopback_mode = efx->loopback_mode;
int phy_mode = efx->phy_mode;
enum reset_type reset_method = RESET_TYPE_INVISIBLE;
struct ethtool_cmd ecmd;
struct efx_channel *channel;
int rc_test = 0, rc_reset = 0, rc;
/* Online (i.e. non-disruptive) testing
* This checks interrupt generation, event delivery and PHY presence. */
rc = efx_test_mdio(efx, tests);
if (rc && !rc_test)
rc_test = rc;
rc = efx_test_nvram(efx, tests);
if (rc && !rc_test)
rc_test = rc;
rc = efx_test_interrupts(efx, tests);
if (rc && !rc_test)
rc_test = rc;
efx_for_each_channel(channel, efx) {
rc = efx_test_eventq_irq(channel, tests);
if (rc && !rc_test)
rc_test = rc;
}
if (rc_test)
return rc_test;
if (!(flags & ETH_TEST_FL_OFFLINE))
return efx_test_phy(efx, tests, flags);
/* Offline (i.e. disruptive) testing
* This checks MAC and PHY loopback on the specified port. */
/* force the carrier state off so the kernel doesn't transmit during
* the loopback test, and the watchdog timeout doesn't fire. Also put
* falcon into loopback for the register test.
*/
mutex_lock(&efx->mac_lock);
efx->port_inhibited = true;
if (efx->loopback_modes) {
/* We need the 312 clock from the PHY to test the XMAC
* registers, so move into XGMII loopback if available */
if (efx->loopback_modes & (1 << LOOPBACK_XGMII))
efx->loopback_mode = LOOPBACK_XGMII;
else
efx->loopback_mode = __ffs(efx->loopback_modes);
}
__efx_reconfigure_port(efx);
mutex_unlock(&efx->mac_lock);
/* free up all consumers of SRAM (including all the queues) */
efx_reset_down(efx, reset_method, &ecmd);
rc = efx_test_chip(efx, tests);
if (rc && !rc_test)
rc_test = rc;
/* reset the chip to recover from the register test */
rc_reset = falcon_reset_hw(efx, reset_method);
/* Ensure that the phy is powered and out of loopback
* for the bist and loopback tests */
efx->phy_mode &= ~PHY_MODE_LOW_POWER;
efx->loopback_mode = LOOPBACK_NONE;
rc = efx_reset_up(efx, reset_method, &ecmd, rc_reset == 0);
if (rc && !rc_reset)
rc_reset = rc;
if (rc_reset) {
EFX_ERR(efx, "Unable to recover from chip test\n");
efx_schedule_reset(efx, RESET_TYPE_DISABLE);
return rc_reset;
}
rc = efx_test_phy(efx, tests, flags);
if (rc && !rc_test)
rc_test = rc;
rc = efx_test_loopbacks(efx, tests, efx->loopback_modes);
if (rc && !rc_test)
rc_test = rc;
/* restore the PHY to the previous state */
efx->loopback_mode = loopback_mode;
efx->phy_mode = phy_mode;
efx->port_inhibited = false;
efx_ethtool_set_settings(efx->net_dev, &ecmd);
return rc_test;
}

View File

@@ -0,0 +1,53 @@
/****************************************************************************
* 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_SELFTEST_H
#define EFX_SELFTEST_H
#include "net_driver.h"
/*
* Self tests
*/
struct efx_loopback_self_tests {
int tx_sent[EFX_TX_QUEUE_COUNT];
int tx_done[EFX_TX_QUEUE_COUNT];
int rx_good;
int rx_bad;
};
#define EFX_MAX_PHY_TESTS 20
/* Efx self test results
* For fields which are not counters, 1 indicates success and -1
* indicates failure.
*/
struct efx_self_tests {
/* online tests */
int mdio;
int nvram;
int interrupt;
int eventq_dma[EFX_MAX_CHANNELS];
int eventq_int[EFX_MAX_CHANNELS];
int eventq_poll[EFX_MAX_CHANNELS];
/* offline tests */
int registers;
int phy[EFX_MAX_PHY_TESTS];
struct efx_loopback_self_tests loopback[LOOPBACK_TEST_MAX + 1];
};
extern void efx_loopback_rx_packet(struct efx_nic *efx,
const char *buf_ptr, int pkt_len);
extern int efx_selftest(struct efx_nic *efx,
struct efx_self_tests *tests,
unsigned flags);
#endif /* EFX_SELFTEST_H */

View File

@@ -0,0 +1,435 @@
/****************************************************************************
* Driver for Solarflare Solarstorm network controllers and boards
* Copyright 2007-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.
*/
/*****************************************************************************
* Support for the SFE4001 and SFN4111T NICs.
*
* The SFE4001 does not power-up fully at reset due to its high power
* consumption. We control its power via a PCA9539 I/O expander.
* Both boards have a MAX6647 temperature monitor which we expose to
* the lm90 driver.
*
* This also provides minimal support for reflashing the PHY, which is
* initiated by resetting it with the FLASH_CFG_1 pin pulled down.
* On SFE4001 rev A2 and later this is connected to the 3V3X output of
* the IO-expander; on the SFN4111T it is connected to Falcon's GPIO3.
* We represent reflash mode as PHY_MODE_SPECIAL and make it mutually
* exclusive with the network device being open.
*/
#include <linux/delay.h>
#include <linux/rtnetlink.h>
#include "net_driver.h"
#include "efx.h"
#include "phy.h"
#include "boards.h"
#include "falcon.h"
#include "falcon_hwdefs.h"
#include "falcon_io.h"
#include "mac.h"
#include "workarounds.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 MAX664X_REG_RSL 0x02
#define MAX664X_REG_WLHO 0x0B
static void sfe4001_poweroff(struct efx_nic *efx)
{
struct i2c_client *ioexp_client = efx->board_info.ioexp_client;
struct i2c_client *hwmon_client = efx->board_info.hwmon_client;
/* Turn off all power rails and disable outputs */
i2c_smbus_write_byte_data(ioexp_client, P0_OUT, 0xff);
i2c_smbus_write_byte_data(ioexp_client, P1_CONFIG, 0xff);
i2c_smbus_write_byte_data(ioexp_client, P0_CONFIG, 0xff);
/* Clear any over-temperature alert */
i2c_smbus_read_byte_data(hwmon_client, MAX664X_REG_RSL);
}
static int sfe4001_poweron(struct efx_nic *efx)
{
struct i2c_client *hwmon_client = efx->board_info.hwmon_client;
struct i2c_client *ioexp_client = efx->board_info.ioexp_client;
unsigned int i, j;
int rc;
u8 out;
/* Clear any previous over-temperature alert */
rc = i2c_smbus_read_byte_data(hwmon_client, MAX664X_REG_RSL);
if (rc < 0)
return rc;
/* Enable port 0 and port 1 outputs on IO expander */
rc = i2c_smbus_write_byte_data(ioexp_client, P0_CONFIG, 0x00);
if (rc)
return rc;
rc = i2c_smbus_write_byte_data(ioexp_client, P1_CONFIG,
0xff & ~(1 << P1_SPARE_LBN));
if (rc)
goto fail_on;
/* If PHY power is on, turn it all off and wait 1 second to
* ensure a full reset.
*/
rc = i2c_smbus_read_byte_data(ioexp_client, P0_OUT);
if (rc < 0)
goto fail_on;
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));
if (rc != out) {
EFX_INFO(efx, "power-cycling PHY\n");
rc = i2c_smbus_write_byte_data(ioexp_client, P0_OUT, out);
if (rc)
goto fail_on;
schedule_timeout_uninterruptible(HZ);
}
for (i = 0; i < 20; ++i) {
/* 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));
if (efx->phy_mode & PHY_MODE_SPECIAL)
out |= 1 << P0_EN_3V3X_LBN;
rc = i2c_smbus_write_byte_data(ioexp_client, P0_OUT, out);
if (rc)
goto fail_on;
msleep(10);
/* Turn on 1V power rail */
out &= ~(1 << P0_EN_1V0X_LBN);
rc = i2c_smbus_write_byte_data(ioexp_client, P0_OUT, out);
if (rc)
goto fail_on;
EFX_INFO(efx, "waiting for DSP boot (attempt %d)...\n", i);
/* In flash config mode, DSP does not turn on AFE, so
* just wait 1 second.
*/
if (efx->phy_mode & PHY_MODE_SPECIAL) {
schedule_timeout_uninterruptible(HZ);
return 0;
}
for (j = 0; j < 10; ++j) {
msleep(100);
/* Check DSP has asserted AFE power line */
rc = i2c_smbus_read_byte_data(ioexp_client, P1_IN);
if (rc < 0)
goto fail_on;
if (rc & (1 << P1_AFE_PWD_LBN))
return 0;
}
}
EFX_INFO(efx, "timed out waiting for DSP boot\n");
rc = -ETIMEDOUT;
fail_on:
sfe4001_poweroff(efx);
return rc;
}
static int sfn4111t_reset(struct efx_nic *efx)
{
efx_oword_t reg;
/* GPIO 3 and the GPIO register are shared with I2C, so block that */
i2c_lock_adapter(&efx->i2c_adap);
/* Pull RST_N (GPIO 2) low then let it up again, setting the
* FLASH_CFG_1 strap (GPIO 3) appropriately. Only change the
* output enables; the output levels should always be 0 (low)
* and we rely on external pull-ups. */
falcon_read(efx, &reg, GPIO_CTL_REG_KER);
EFX_SET_OWORD_FIELD(reg, GPIO2_OEN, true);
falcon_write(efx, &reg, GPIO_CTL_REG_KER);
msleep(1000);
EFX_SET_OWORD_FIELD(reg, GPIO2_OEN, false);
EFX_SET_OWORD_FIELD(reg, GPIO3_OEN,
!!(efx->phy_mode & PHY_MODE_SPECIAL));
falcon_write(efx, &reg, GPIO_CTL_REG_KER);
msleep(1);
i2c_unlock_adapter(&efx->i2c_adap);
ssleep(1);
return 0;
}
static ssize_t show_phy_flash_cfg(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct efx_nic *efx = pci_get_drvdata(to_pci_dev(dev));
return sprintf(buf, "%d\n", !!(efx->phy_mode & PHY_MODE_SPECIAL));
}
static ssize_t set_phy_flash_cfg(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
struct efx_nic *efx = pci_get_drvdata(to_pci_dev(dev));
enum efx_phy_mode old_mode, new_mode;
int err;
rtnl_lock();
old_mode = efx->phy_mode;
if (count == 0 || *buf == '0')
new_mode = old_mode & ~PHY_MODE_SPECIAL;
else
new_mode = PHY_MODE_SPECIAL;
if (old_mode == new_mode) {
err = 0;
} else if (efx->state != STATE_RUNNING || netif_running(efx->net_dev)) {
err = -EBUSY;
} else {
/* Reset the PHY, reconfigure the MAC and enable/disable
* MAC stats accordingly. */
efx->phy_mode = new_mode;
if (new_mode & PHY_MODE_SPECIAL)
efx_stats_disable(efx);
if (efx->board_info.type == EFX_BOARD_SFE4001)
err = sfe4001_poweron(efx);
else
err = sfn4111t_reset(efx);
efx_reconfigure_port(efx);
if (!(new_mode & PHY_MODE_SPECIAL))
efx_stats_enable(efx);
}
rtnl_unlock();
return err ? err : count;
}
static DEVICE_ATTR(phy_flash_cfg, 0644, show_phy_flash_cfg, set_phy_flash_cfg);
static void sfe4001_fini(struct efx_nic *efx)
{
EFX_INFO(efx, "%s\n", __func__);
device_remove_file(&efx->pci_dev->dev, &dev_attr_phy_flash_cfg);
sfe4001_poweroff(efx);
i2c_unregister_device(efx->board_info.ioexp_client);
i2c_unregister_device(efx->board_info.hwmon_client);
}
static int sfe4001_check_hw(struct efx_nic *efx)
{
s32 status;
/* If XAUI link is up then do not monitor */
if (EFX_WORKAROUND_7884(efx) && efx->mac_up)
return 0;
/* Check the powered status of the PHY. Lack of power implies that
* the MAX6647 has shut down power to it, probably due to a temp.
* alarm. Reading the power status rather than the MAX6647 status
* directly because the later is read-to-clear and would thus
* start to power up the PHY again when polled, causing us to blip
* the power undesirably.
* We know we can read from the IO expander because we did
* it during power-on. Assume failure now is bad news. */
status = i2c_smbus_read_byte_data(efx->board_info.ioexp_client, P1_IN);
if (status >= 0 &&
(status & ((1 << P1_AFE_PWD_LBN) | (1 << P1_DSP_PWD25_LBN))) != 0)
return 0;
/* Use board power control, not PHY power control */
sfe4001_poweroff(efx);
efx->phy_mode = PHY_MODE_OFF;
return (status < 0) ? -EIO : -ERANGE;
}
static struct i2c_board_info sfe4001_hwmon_info = {
I2C_BOARD_INFO("max6647", 0x4e),
};
/* 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_init(struct efx_nic *efx)
{
int rc;
#if defined(CONFIG_SENSORS_LM90) || defined(CONFIG_SENSORS_LM90_MODULE)
efx->board_info.hwmon_client =
i2c_new_device(&efx->i2c_adap, &sfe4001_hwmon_info);
#else
efx->board_info.hwmon_client =
i2c_new_dummy(&efx->i2c_adap, sfe4001_hwmon_info.addr);
#endif
if (!efx->board_info.hwmon_client)
return -EIO;
/* Raise board/PHY high limit from 85 to 90 degrees Celsius */
rc = i2c_smbus_write_byte_data(efx->board_info.hwmon_client,
MAX664X_REG_WLHO, 90);
if (rc)
goto fail_hwmon;
efx->board_info.ioexp_client = i2c_new_dummy(&efx->i2c_adap, PCA9539);
if (!efx->board_info.ioexp_client) {
rc = -EIO;
goto fail_hwmon;
}
/* 10Xpress has fixed-function LED pins, so there is no board-specific
* blink code. */
efx->board_info.blink = tenxpress_phy_blink;
efx->board_info.monitor = sfe4001_check_hw;
efx->board_info.fini = sfe4001_fini;
if (efx->phy_mode & PHY_MODE_SPECIAL) {
/* PHY won't generate a 156.25 MHz clock and MAC stats fetch
* will fail. */
efx_stats_disable(efx);
}
rc = sfe4001_poweron(efx);
if (rc)
goto fail_ioexp;
rc = device_create_file(&efx->pci_dev->dev, &dev_attr_phy_flash_cfg);
if (rc)
goto fail_on;
EFX_INFO(efx, "PHY is powered on\n");
return 0;
fail_on:
sfe4001_poweroff(efx);
fail_ioexp:
i2c_unregister_device(efx->board_info.ioexp_client);
fail_hwmon:
i2c_unregister_device(efx->board_info.hwmon_client);
return rc;
}
static int sfn4111t_check_hw(struct efx_nic *efx)
{
s32 status;
/* If XAUI link is up then do not monitor */
if (EFX_WORKAROUND_7884(efx) && efx->mac_up)
return 0;
/* Test LHIGH, RHIGH, FAULT, EOT and IOT alarms */
status = i2c_smbus_read_byte_data(efx->board_info.hwmon_client,
MAX664X_REG_RSL);
if (status < 0)
return -EIO;
if (status & 0x57)
return -ERANGE;
return 0;
}
static void sfn4111t_fini(struct efx_nic *efx)
{
EFX_INFO(efx, "%s\n", __func__);
device_remove_file(&efx->pci_dev->dev, &dev_attr_phy_flash_cfg);
i2c_unregister_device(efx->board_info.hwmon_client);
}
static struct i2c_board_info sfn4111t_a0_hwmon_info = {
I2C_BOARD_INFO("max6647", 0x4e),
};
static struct i2c_board_info sfn4111t_r5_hwmon_info = {
I2C_BOARD_INFO("max6646", 0x4d),
};
int sfn4111t_init(struct efx_nic *efx)
{
int i = 0;
int rc;
efx->board_info.hwmon_client =
i2c_new_device(&efx->i2c_adap,
(efx->board_info.minor < 5) ?
&sfn4111t_a0_hwmon_info :
&sfn4111t_r5_hwmon_info);
if (!efx->board_info.hwmon_client)
return -EIO;
efx->board_info.blink = tenxpress_phy_blink;
efx->board_info.monitor = sfn4111t_check_hw;
efx->board_info.fini = sfn4111t_fini;
rc = device_create_file(&efx->pci_dev->dev, &dev_attr_phy_flash_cfg);
if (rc)
goto fail_hwmon;
do {
if (efx->phy_mode & PHY_MODE_SPECIAL) {
/* PHY may not generate a 156.25 MHz clock and MAC
* stats fetch will fail. */
efx_stats_disable(efx);
sfn4111t_reset(efx);
}
rc = sft9001_wait_boot(efx);
if (rc == 0)
return 0;
efx->phy_mode = PHY_MODE_SPECIAL;
} while (rc == -EINVAL && ++i < 2);
device_remove_file(&efx->pci_dev->dev, &dev_attr_phy_flash_cfg);
fail_hwmon:
i2c_unregister_device(efx->board_info.hwmon_client);
return rc;
}

View File

@@ -0,0 +1,96 @@
/****************************************************************************
* 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
*
*************************************************************************/
#define SPI_WRSR 0x01 /* Write status register */
#define SPI_WRITE 0x02 /* Write data to memory array */
#define SPI_READ 0x03 /* Read data from memory array */
#define SPI_WRDI 0x04 /* Reset write enable latch */
#define SPI_RDSR 0x05 /* Read status register */
#define SPI_WREN 0x06 /* Set write enable latch */
#define SPI_SST_EWSR 0x50 /* SST: Enable write to status register */
#define SPI_STATUS_WPEN 0x80 /* Write-protect pin enabled */
#define SPI_STATUS_BP2 0x10 /* Block protection bit 2 */
#define SPI_STATUS_BP1 0x08 /* Block protection bit 1 */
#define SPI_STATUS_BP0 0x04 /* Block protection bit 0 */
#define SPI_STATUS_WEN 0x02 /* State of the write enable latch */
#define SPI_STATUS_NRDY 0x01 /* Device busy flag */
/**
* struct efx_spi_device - an Efx SPI (Serial Peripheral Interface) device
* @efx: The Efx controller that owns this device
* @mtd: MTD state
* @device_id: Controller's id for the device
* @size: Size (in bytes)
* @addr_len: Number of address bytes in read/write commands
* @munge_address: Flag whether addresses should be munged.
* Some devices with 9-bit addresses (e.g. AT25040A EEPROM)
* use bit 3 of the command byte as address bit A8, rather
* than having a two-byte address. If this flag is set, then
* commands should be munged in this way.
* @erase_command: Erase command (or 0 if sector erase not needed).
* @erase_size: Erase sector size (in bytes)
* Erase commands affect sectors with this size and alignment.
* This must be a power of two.
* @block_size: Write block size (in bytes).
* Write commands are limited to blocks with this size and alignment.
*/
struct efx_spi_device {
struct efx_nic *efx;
#ifdef CONFIG_SFC_MTD
void *mtd;
#endif
int device_id;
unsigned int size;
unsigned int addr_len;
unsigned int munge_address:1;
u8 erase_command;
unsigned int erase_size;
unsigned int block_size;
};
int falcon_spi_cmd(const struct efx_spi_device *spi, unsigned int command,
int address, const void* in, void *out, size_t len);
int falcon_spi_wait_write(const struct efx_spi_device *spi);
int falcon_spi_read(const struct efx_spi_device *spi, loff_t start,
size_t len, size_t *retlen, u8 *buffer);
int falcon_spi_write(const struct efx_spi_device *spi, loff_t start,
size_t len, size_t *retlen, const u8 *buffer);
/*
* SFC4000 flash is partitioned into:
* 0-0x400 chip and board config (see falcon_hwdefs.h)
* 0x400-0x8000 unused (or may contain VPD if EEPROM not present)
* 0x8000-end boot code (mapped to PCI expansion ROM)
* SFC4000 small EEPROM (size < 0x400) is used for VPD only.
* SFC4000 large EEPROM (size >= 0x400) is partitioned into:
* 0-0x400 chip and board config
* configurable VPD
* 0x800-0x1800 boot config
* Aside from the chip and board config, all of these are optional and may
* be absent or truncated depending on the devices used.
*/
#define FALCON_NVCONFIG_END 0x400U
#define FALCON_FLASH_BOOTCODE_START 0x8000U
#define EFX_EEPROM_BOOTCONFIG_START 0x800U
#define EFX_EEPROM_BOOTCONFIG_END 0x1800U
#endif /* EFX_SPI_H */

View File

@@ -0,0 +1,822 @@
/****************************************************************************
* Driver for Solarflare Solarstorm network controllers and boards
* Copyright 2007-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 <linux/rtnetlink.h>
#include <linux/seq_file.h>
#include "efx.h"
#include "mdio_10g.h"
#include "falcon.h"
#include "phy.h"
#include "falcon_hwdefs.h"
#include "boards.h"
#include "workarounds.h"
#include "selftest.h"
/* We expect these MMDs to be in the package. SFT9001 also has a
* clause 22 extension MMD, but since it doesn't have all the generic
* MMD registers it is pointless to include it here.
*/
#define TENXPRESS_REQUIRED_DEVS (MDIO_DEVS_PMAPMD | \
MDIO_DEVS_PCS | \
MDIO_DEVS_PHYXS | \
MDIO_DEVS_AN)
#define SFX7101_LOOPBACKS ((1 << LOOPBACK_PHYXS) | \
(1 << LOOPBACK_PCS) | \
(1 << LOOPBACK_PMAPMD) | \
(1 << LOOPBACK_NETWORK))
#define SFT9001_LOOPBACKS ((1 << LOOPBACK_GPHY) | \
(1 << LOOPBACK_PHYXS) | \
(1 << LOOPBACK_PCS) | \
(1 << LOOPBACK_PMAPMD) | \
(1 << LOOPBACK_NETWORK))
/* 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 49152
#define PMA_PMD_EXT_GMII_EN_LBN 1
#define PMA_PMD_EXT_GMII_EN_WIDTH 1
#define PMA_PMD_EXT_CLK_OUT_LBN 2
#define PMA_PMD_EXT_CLK_OUT_WIDTH 1
#define PMA_PMD_LNPGA_POWERDOWN_LBN 8 /* SFX7101 only */
#define PMA_PMD_LNPGA_POWERDOWN_WIDTH 1
#define PMA_PMD_EXT_CLK312_LBN 8 /* SFT9001 only */
#define PMA_PMD_EXT_CLK312_WIDTH 1
#define PMA_PMD_EXT_LPOWER_LBN 12
#define PMA_PMD_EXT_LPOWER_WIDTH 1
#define PMA_PMD_EXT_ROBUST_LBN 14
#define PMA_PMD_EXT_ROBUST_WIDTH 1
#define PMA_PMD_EXT_SSR_LBN 15
#define PMA_PMD_EXT_SSR_WIDTH 1
/* extended status register */
#define PMA_PMD_XSTATUS_REG 49153
#define PMA_PMD_XSTAT_MDIX_LBN 14
#define PMA_PMD_XSTAT_FLP_LBN (12)
/* LED control register */
#define PMA_PMD_LED_CTRL_REG 49159
#define PMA_PMA_LED_ACTIVITY_LBN (3)
/* LED function override register */
#define PMA_PMD_LED_OVERR_REG 49161
/* 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)
#define PMA_PMD_LED_MASK 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)
#define PMA_PMD_SPEED_ENABLE_REG 49192
#define PMA_PMD_100TX_ADV_LBN 1
#define PMA_PMD_100TX_ADV_WIDTH 1
#define PMA_PMD_1000T_ADV_LBN 2
#define PMA_PMD_1000T_ADV_WIDTH 1
#define PMA_PMD_10000T_ADV_LBN 3
#define PMA_PMD_10000T_ADV_WIDTH 1
#define PMA_PMD_SPEED_LBN 4
#define PMA_PMD_SPEED_WIDTH 4
/* Cable diagnostics - SFT9001 only */
#define PMA_PMD_CDIAG_CTRL_REG 49213
#define CDIAG_CTRL_IMMED_LBN 15
#define CDIAG_CTRL_BRK_LINK_LBN 12
#define CDIAG_CTRL_IN_PROG_LBN 11
#define CDIAG_CTRL_LEN_UNIT_LBN 10
#define CDIAG_CTRL_LEN_METRES 1
#define PMA_PMD_CDIAG_RES_REG 49174
#define CDIAG_RES_A_LBN 12
#define CDIAG_RES_B_LBN 8
#define CDIAG_RES_C_LBN 4
#define CDIAG_RES_D_LBN 0
#define CDIAG_RES_WIDTH 4
#define CDIAG_RES_OPEN 2
#define CDIAG_RES_OK 1
#define CDIAG_RES_INVALID 0
/* Set of 4 registers for pairs A-D */
#define PMA_PMD_CDIAG_LEN_REG 49175
/* Serdes control registers - SFT9001 only */
#define PMA_PMD_CSERDES_CTRL_REG 64258
/* Set the 156.25 MHz output to 312.5 MHz to drive Falcon's XMAC */
#define PMA_PMD_CSERDES_DEFAULT 0x000f
/* Misc register defines - SFX7101 only */
#define PCS_CLOCK_CTRL_REG 55297
#define PLL312_RST_N_LBN 2
#define PCS_SOFT_RST2_REG 55302
#define SERDES_RST_N_LBN 13
#define XGXS_RST_N_LBN 12
#define PCS_TEST_SELECT_REG 55303 /* PRM 10.5.8 */
#define CLK312_EN_LBN 3
/* PHYXS registers */
#define PHYXS_XCONTROL_REG 49152
#define PHYXS_RESET_LBN 15
#define PHYXS_RESET_WIDTH 1
#define PHYXS_TEST1 (49162)
#define LOOPBACK_NEAR_LBN (8)
#define LOOPBACK_NEAR_WIDTH (1)
/* Boot status register */
#define PCS_BOOT_STATUS_REG 53248
#define PCS_BOOT_FATAL_ERROR_LBN 0
#define PCS_BOOT_PROGRESS_LBN 1
#define PCS_BOOT_PROGRESS_WIDTH 2
#define PCS_BOOT_PROGRESS_INIT 0
#define PCS_BOOT_PROGRESS_WAIT_MDIO 1
#define PCS_BOOT_PROGRESS_CHECKSUM 2
#define PCS_BOOT_PROGRESS_JUMP 3
#define PCS_BOOT_DOWNLOAD_WAIT_LBN 3
#define PCS_BOOT_CODE_STARTED_LBN 4
/* 100M/1G PHY registers */
#define GPHY_XCONTROL_REG 49152
#define GPHY_ISOLATE_LBN 10
#define GPHY_ISOLATE_WIDTH 1
#define GPHY_DUPLEX_LBN 8
#define GPHY_DUPLEX_WIDTH 1
#define GPHY_LOOPBACK_NEAR_LBN 14
#define GPHY_LOOPBACK_NEAR_WIDTH 1
#define C22EXT_STATUS_REG 49153
#define C22EXT_STATUS_LINK_LBN 2
#define C22EXT_STATUS_LINK_WIDTH 1
#define C22EXT_MSTSLV_CTRL 49161
#define C22EXT_MSTSLV_CTRL_ADV_1000_HD_LBN 8
#define C22EXT_MSTSLV_CTRL_ADV_1000_FD_LBN 9
#define C22EXT_MSTSLV_STATUS 49162
#define C22EXT_MSTSLV_STATUS_LP_1000_HD_LBN 10
#define C22EXT_MSTSLV_STATUS_LP_1000_FD_LBN 11
/* Time to wait between powering down the LNPGA and turning off the power
* rails */
#define LNPGA_PDOWN_WAIT (HZ / 5)
struct tenxpress_phy_data {
enum efx_loopback_mode loopback_mode;
enum efx_phy_mode phy_mode;
int bad_lp_tries;
};
static ssize_t show_phy_short_reach(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct efx_nic *efx = pci_get_drvdata(to_pci_dev(dev));
int reg;
reg = efx_mdio_read(efx, MDIO_MMD_PMAPMD, MDIO_PMA_10GBT_TXPWR);
return sprintf(buf, "%d\n", !!(reg & MDIO_PMA_10GBT_TXPWR_SHORT));
}
static ssize_t set_phy_short_reach(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
struct efx_nic *efx = pci_get_drvdata(to_pci_dev(dev));
rtnl_lock();
efx_mdio_set_flag(efx, MDIO_MMD_PMAPMD, MDIO_PMA_10GBT_TXPWR,
MDIO_PMA_10GBT_TXPWR_SHORT,
count != 0 && *buf != '0');
efx_reconfigure_port(efx);
rtnl_unlock();
return count;
}
static DEVICE_ATTR(phy_short_reach, 0644, show_phy_short_reach,
set_phy_short_reach);
int sft9001_wait_boot(struct efx_nic *efx)
{
unsigned long timeout = jiffies + HZ + 1;
int boot_stat;
for (;;) {
boot_stat = efx_mdio_read(efx, MDIO_MMD_PCS,
PCS_BOOT_STATUS_REG);
if (boot_stat >= 0) {
EFX_LOG(efx, "PHY boot status = %#x\n", boot_stat);
switch (boot_stat &
((1 << PCS_BOOT_FATAL_ERROR_LBN) |
(3 << PCS_BOOT_PROGRESS_LBN) |
(1 << PCS_BOOT_DOWNLOAD_WAIT_LBN) |
(1 << PCS_BOOT_CODE_STARTED_LBN))) {
case ((1 << PCS_BOOT_FATAL_ERROR_LBN) |
(PCS_BOOT_PROGRESS_CHECKSUM <<
PCS_BOOT_PROGRESS_LBN)):
case ((1 << PCS_BOOT_FATAL_ERROR_LBN) |
(PCS_BOOT_PROGRESS_INIT <<
PCS_BOOT_PROGRESS_LBN) |
(1 << PCS_BOOT_DOWNLOAD_WAIT_LBN)):
return -EINVAL;
case ((PCS_BOOT_PROGRESS_WAIT_MDIO <<
PCS_BOOT_PROGRESS_LBN) |
(1 << PCS_BOOT_DOWNLOAD_WAIT_LBN)):
return (efx->phy_mode & PHY_MODE_SPECIAL) ?
0 : -EIO;
case ((PCS_BOOT_PROGRESS_JUMP <<
PCS_BOOT_PROGRESS_LBN) |
(1 << PCS_BOOT_CODE_STARTED_LBN)):
case ((PCS_BOOT_PROGRESS_JUMP <<
PCS_BOOT_PROGRESS_LBN) |
(1 << PCS_BOOT_DOWNLOAD_WAIT_LBN) |
(1 << PCS_BOOT_CODE_STARTED_LBN)):
return (efx->phy_mode & PHY_MODE_SPECIAL) ?
-EIO : 0;
default:
if (boot_stat & (1 << PCS_BOOT_FATAL_ERROR_LBN))
return -EIO;
break;
}
}
if (time_after_eq(jiffies, timeout))
return -ETIMEDOUT;
msleep(50);
}
}
static int tenxpress_init(struct efx_nic *efx)
{
int reg;
if (efx->phy_type == PHY_TYPE_SFX7101) {
/* Enable 312.5 MHz clock */
efx_mdio_write(efx, MDIO_MMD_PCS, PCS_TEST_SELECT_REG,
1 << CLK312_EN_LBN);
} else {
/* Enable 312.5 MHz clock and GMII */
reg = efx_mdio_read(efx, MDIO_MMD_PMAPMD, PMA_PMD_XCONTROL_REG);
reg |= ((1 << PMA_PMD_EXT_GMII_EN_LBN) |
(1 << PMA_PMD_EXT_CLK_OUT_LBN) |
(1 << PMA_PMD_EXT_CLK312_LBN) |
(1 << PMA_PMD_EXT_ROBUST_LBN));
efx_mdio_write(efx, MDIO_MMD_PMAPMD, PMA_PMD_XCONTROL_REG, reg);
efx_mdio_set_flag(efx, MDIO_MMD_C22EXT,
GPHY_XCONTROL_REG, 1 << GPHY_ISOLATE_LBN,
false);
}
/* Set the LEDs up as: Green = Link, Amber = Link/Act, Red = Off */
if (efx->phy_type == PHY_TYPE_SFX7101) {
efx_mdio_set_flag(efx, MDIO_MMD_PMAPMD, PMA_PMD_LED_CTRL_REG,
1 << PMA_PMA_LED_ACTIVITY_LBN, true);
efx_mdio_write(efx, MDIO_MMD_PMAPMD, PMA_PMD_LED_OVERR_REG,
PMA_PMD_LED_DEFAULT);
}
return 0;
}
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);
if (!phy_data)
return -ENOMEM;
efx->phy_data = phy_data;
phy_data->phy_mode = efx->phy_mode;
if (!(efx->phy_mode & PHY_MODE_SPECIAL)) {
if (efx->phy_type == PHY_TYPE_SFT9001A) {
int reg;
reg = efx_mdio_read(efx, MDIO_MMD_PMAPMD,
PMA_PMD_XCONTROL_REG);
reg |= (1 << PMA_PMD_EXT_SSR_LBN);
efx_mdio_write(efx, MDIO_MMD_PMAPMD,
PMA_PMD_XCONTROL_REG, reg);
mdelay(200);
}
rc = efx_mdio_wait_reset_mmds(efx, TENXPRESS_REQUIRED_DEVS);
if (rc < 0)
goto fail;
rc = efx_mdio_check_mmds(efx, TENXPRESS_REQUIRED_DEVS, 0);
if (rc < 0)
goto fail;
}
rc = tenxpress_init(efx);
if (rc < 0)
goto fail;
if (efx->phy_type == PHY_TYPE_SFT9001B) {
rc = device_create_file(&efx->pci_dev->dev,
&dev_attr_phy_short_reach);
if (rc)
goto fail;
}
schedule_timeout_uninterruptible(HZ / 5); /* 200ms */
/* Let XGXS and SerDes out of reset */
falcon_reset_xaui(efx);
return 0;
fail:
kfree(efx->phy_data);
efx->phy_data = NULL;
return rc;
}
/* Perform a "special software reset" on the PHY. The caller is
* responsible for saving and restoring the PHY hardware registers
* properly, and masking/unmasking LASI */
static int tenxpress_special_reset(struct efx_nic *efx)
{
int rc, reg;
/* The XGMAC clock is driven from the SFC7101/SFT9001 312MHz clock, so
* a special software reset can glitch the XGMAC sufficiently for stats
* requests to fail. */
efx_stats_disable(efx);
/* Initiate reset */
reg = efx_mdio_read(efx, MDIO_MMD_PMAPMD, PMA_PMD_XCONTROL_REG);
reg |= (1 << PMA_PMD_EXT_SSR_LBN);
efx_mdio_write(efx, MDIO_MMD_PMAPMD, PMA_PMD_XCONTROL_REG, reg);
mdelay(200);
/* Wait for the blocks to come out of reset */
rc = efx_mdio_wait_reset_mmds(efx, TENXPRESS_REQUIRED_DEVS);
if (rc < 0)
goto out;
/* Try and reconfigure the device */
rc = tenxpress_init(efx);
if (rc < 0)
goto out;
/* Wait for the XGXS state machine to churn */
mdelay(10);
out:
efx_stats_enable(efx);
return rc;
}
static void sfx7101_check_bad_lp(struct efx_nic *efx, bool link_ok)
{
struct tenxpress_phy_data *pd = efx->phy_data;
bool bad_lp;
int reg;
if (link_ok) {
bad_lp = false;
} else {
/* Check that AN has started but not completed. */
reg = efx_mdio_read(efx, MDIO_MMD_AN, MDIO_STAT1);
if (!(reg & MDIO_AN_STAT1_LPABLE))
return; /* LP status is unknown */
bad_lp = !(reg & MDIO_AN_STAT1_COMPLETE);
if (bad_lp)
pd->bad_lp_tries++;
}
/* Nothing to do if all is well and was previously so. */
if (!pd->bad_lp_tries)
return;
/* Use the RX (red) LED as an error indicator once we've seen AN
* failure several times in a row, and also log a message. */
if (!bad_lp || pd->bad_lp_tries == MAX_BAD_LP_TRIES) {
reg = efx_mdio_read(efx, MDIO_MMD_PMAPMD,
PMA_PMD_LED_OVERR_REG);
reg &= ~(PMA_PMD_LED_MASK << PMA_PMD_LED_RX_LBN);
if (!bad_lp) {
reg |= PMA_PMD_LED_OFF << PMA_PMD_LED_RX_LBN;
} else {
reg |= PMA_PMD_LED_FLASH << PMA_PMD_LED_RX_LBN;
EFX_ERR(efx, "appears to be plugged into a port"
" that is not 10GBASE-T capable. The PHY"
" supports 10GBASE-T ONLY, so no link can"
" be established\n");
}
efx_mdio_write(efx, MDIO_MMD_PMAPMD,
PMA_PMD_LED_OVERR_REG, reg);
pd->bad_lp_tries = bad_lp;
}
}
static bool sfx7101_link_ok(struct efx_nic *efx)
{
return efx_mdio_links_ok(efx,
MDIO_DEVS_PMAPMD |
MDIO_DEVS_PCS |
MDIO_DEVS_PHYXS);
}
static bool sft9001_link_ok(struct efx_nic *efx, struct ethtool_cmd *ecmd)
{
u32 reg;
if (efx_phy_mode_disabled(efx->phy_mode))
return false;
else if (efx->loopback_mode == LOOPBACK_GPHY)
return true;
else if (efx->loopback_mode)
return efx_mdio_links_ok(efx,
MDIO_DEVS_PMAPMD |
MDIO_DEVS_PHYXS);
/* We must use the same definition of link state as LASI,
* otherwise we can miss a link state transition
*/
if (ecmd->speed == 10000) {
reg = efx_mdio_read(efx, MDIO_MMD_PCS, MDIO_PCS_10GBRT_STAT1);
return reg & MDIO_PCS_10GBRT_STAT1_BLKLK;
} else {
reg = efx_mdio_read(efx, MDIO_MMD_C22EXT, C22EXT_STATUS_REG);
return reg & (1 << C22EXT_STATUS_LINK_LBN);
}
}
static void tenxpress_ext_loopback(struct efx_nic *efx)
{
efx_mdio_set_flag(efx, MDIO_MMD_PHYXS, PHYXS_TEST1,
1 << LOOPBACK_NEAR_LBN,
efx->loopback_mode == LOOPBACK_PHYXS);
if (efx->phy_type != PHY_TYPE_SFX7101)
efx_mdio_set_flag(efx, MDIO_MMD_C22EXT, GPHY_XCONTROL_REG,
1 << GPHY_LOOPBACK_NEAR_LBN,
efx->loopback_mode == LOOPBACK_GPHY);
}
static void tenxpress_low_power(struct efx_nic *efx)
{
if (efx->phy_type == PHY_TYPE_SFX7101)
efx_mdio_set_mmds_lpower(
efx, !!(efx->phy_mode & PHY_MODE_LOW_POWER),
TENXPRESS_REQUIRED_DEVS);
else
efx_mdio_set_flag(
efx, MDIO_MMD_PMAPMD, PMA_PMD_XCONTROL_REG,
1 << PMA_PMD_EXT_LPOWER_LBN,
!!(efx->phy_mode & PHY_MODE_LOW_POWER));
}
static void tenxpress_phy_reconfigure(struct efx_nic *efx)
{
struct tenxpress_phy_data *phy_data = efx->phy_data;
struct ethtool_cmd ecmd;
bool phy_mode_change, loop_reset;
if (efx->phy_mode & (PHY_MODE_OFF | PHY_MODE_SPECIAL)) {
phy_data->phy_mode = efx->phy_mode;
return;
}
tenxpress_low_power(efx);
phy_mode_change = (efx->phy_mode == PHY_MODE_NORMAL &&
phy_data->phy_mode != PHY_MODE_NORMAL);
loop_reset = (LOOPBACK_OUT_OF(phy_data, efx, efx->phy_op->loopbacks) ||
LOOPBACK_CHANGED(phy_data, efx, 1 << LOOPBACK_GPHY));
if (loop_reset || phy_mode_change) {
int rc;
efx->phy_op->get_settings(efx, &ecmd);
if (loop_reset || phy_mode_change) {
tenxpress_special_reset(efx);
/* Reset XAUI if we were in 10G, and are staying
* in 10G. If we're moving into and out of 10G
* then xaui will be reset anyway */
if (EFX_IS10G(efx))
falcon_reset_xaui(efx);
}
rc = efx->phy_op->set_settings(efx, &ecmd);
WARN_ON(rc);
}
efx_mdio_transmit_disable(efx);
efx_mdio_phy_reconfigure(efx);
tenxpress_ext_loopback(efx);
phy_data->loopback_mode = efx->loopback_mode;
phy_data->phy_mode = efx->phy_mode;
if (efx->phy_type == PHY_TYPE_SFX7101) {
efx->link_speed = 10000;
efx->link_fd = true;
efx->link_up = sfx7101_link_ok(efx);
} else {
efx->phy_op->get_settings(efx, &ecmd);
efx->link_speed = ecmd.speed;
efx->link_fd = ecmd.duplex == DUPLEX_FULL;
efx->link_up = sft9001_link_ok(efx, &ecmd);
}
efx->link_fc = efx_mdio_get_pause(efx);
}
/* Poll PHY for interrupt */
static void tenxpress_phy_poll(struct efx_nic *efx)
{
struct tenxpress_phy_data *phy_data = efx->phy_data;
bool change = false;
if (efx->phy_type == PHY_TYPE_SFX7101) {
bool link_ok = sfx7101_link_ok(efx);
if (link_ok != efx->link_up) {
change = true;
} else {
unsigned int link_fc = efx_mdio_get_pause(efx);
if (link_fc != efx->link_fc)
change = true;
}
sfx7101_check_bad_lp(efx, link_ok);
} else if (efx->loopback_mode) {
bool link_ok = sft9001_link_ok(efx, NULL);
if (link_ok != efx->link_up)
change = true;
} else {
int status = efx_mdio_read(efx, MDIO_MMD_PMAPMD,
MDIO_PMA_LASI_STAT);
if (status & MDIO_PMA_LASI_LSALARM)
change = true;
}
if (change)
falcon_sim_phy_event(efx);
if (phy_data->phy_mode != PHY_MODE_NORMAL)
return;
}
static void tenxpress_phy_fini(struct efx_nic *efx)
{
int reg;
if (efx->phy_type == PHY_TYPE_SFT9001B)
device_remove_file(&efx->pci_dev->dev,
&dev_attr_phy_short_reach);
if (efx->phy_type == PHY_TYPE_SFX7101) {
/* Power down the LNPGA */
reg = (1 << PMA_PMD_LNPGA_POWERDOWN_LBN);
efx_mdio_write(efx, 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, bool 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;
efx_mdio_write(efx, MDIO_MMD_PMAPMD, PMA_PMD_LED_OVERR_REG, reg);
}
static const char *const sfx7101_test_names[] = {
"bist"
};
static int
sfx7101_run_tests(struct efx_nic *efx, int *results, unsigned flags)
{
int rc;
if (!(flags & ETH_TEST_FL_OFFLINE))
return 0;
/* BIST is automatically run after a special software reset */
rc = tenxpress_special_reset(efx);
results[0] = rc ? -1 : 1;
return rc;
}
static const char *const sft9001_test_names[] = {
"bist",
"cable.pairA.status",
"cable.pairB.status",
"cable.pairC.status",
"cable.pairD.status",
"cable.pairA.length",
"cable.pairB.length",
"cable.pairC.length",
"cable.pairD.length",
};
static int sft9001_run_tests(struct efx_nic *efx, int *results, unsigned flags)
{
struct ethtool_cmd ecmd;
int rc = 0, rc2, i, ctrl_reg, res_reg;
if (flags & ETH_TEST_FL_OFFLINE)
efx->phy_op->get_settings(efx, &ecmd);
/* Initialise cable diagnostic results to unknown failure */
for (i = 1; i < 9; ++i)
results[i] = -1;
/* Run cable diagnostics; wait up to 5 seconds for them to complete.
* A cable fault is not a self-test failure, but a timeout is. */
ctrl_reg = ((1 << CDIAG_CTRL_IMMED_LBN) |
(CDIAG_CTRL_LEN_METRES << CDIAG_CTRL_LEN_UNIT_LBN));
if (flags & ETH_TEST_FL_OFFLINE) {
/* Break the link in order to run full diagnostics. We
* must reset the PHY to resume normal service. */
ctrl_reg |= (1 << CDIAG_CTRL_BRK_LINK_LBN);
}
efx_mdio_write(efx, MDIO_MMD_PMAPMD, PMA_PMD_CDIAG_CTRL_REG,
ctrl_reg);
i = 0;
while (efx_mdio_read(efx, MDIO_MMD_PMAPMD, PMA_PMD_CDIAG_CTRL_REG) &
(1 << CDIAG_CTRL_IN_PROG_LBN)) {
if (++i == 50) {
rc = -ETIMEDOUT;
goto out;
}
msleep(100);
}
res_reg = efx_mdio_read(efx, MDIO_MMD_PMAPMD, PMA_PMD_CDIAG_RES_REG);
for (i = 0; i < 4; i++) {
int pair_res =
(res_reg >> (CDIAG_RES_A_LBN - i * CDIAG_RES_WIDTH))
& ((1 << CDIAG_RES_WIDTH) - 1);
int len_reg = efx_mdio_read(efx, MDIO_MMD_PMAPMD,
PMA_PMD_CDIAG_LEN_REG + i);
if (pair_res == CDIAG_RES_OK)
results[1 + i] = 1;
else if (pair_res == CDIAG_RES_INVALID)
results[1 + i] = -1;
else
results[1 + i] = -pair_res;
if (pair_res != CDIAG_RES_INVALID &&
pair_res != CDIAG_RES_OPEN &&
len_reg != 0xffff)
results[5 + i] = len_reg;
}
out:
if (flags & ETH_TEST_FL_OFFLINE) {
/* Reset, running the BIST and then resuming normal service. */
rc2 = tenxpress_special_reset(efx);
results[0] = rc2 ? -1 : 1;
if (!rc)
rc = rc2;
rc2 = efx->phy_op->set_settings(efx, &ecmd);
if (!rc)
rc = rc2;
}
return rc;
}
static void
tenxpress_get_settings(struct efx_nic *efx, struct ethtool_cmd *ecmd)
{
u32 adv = 0, lpa = 0;
int reg;
if (efx->phy_type != PHY_TYPE_SFX7101) {
reg = efx_mdio_read(efx, MDIO_MMD_C22EXT, C22EXT_MSTSLV_CTRL);
if (reg & (1 << C22EXT_MSTSLV_CTRL_ADV_1000_FD_LBN))
adv |= ADVERTISED_1000baseT_Full;
reg = efx_mdio_read(efx, MDIO_MMD_C22EXT, C22EXT_MSTSLV_STATUS);
if (reg & (1 << C22EXT_MSTSLV_STATUS_LP_1000_HD_LBN))
lpa |= ADVERTISED_1000baseT_Half;
if (reg & (1 << C22EXT_MSTSLV_STATUS_LP_1000_FD_LBN))
lpa |= ADVERTISED_1000baseT_Full;
}
reg = efx_mdio_read(efx, MDIO_MMD_AN, MDIO_AN_10GBT_CTRL);
if (reg & MDIO_AN_10GBT_CTRL_ADV10G)
adv |= ADVERTISED_10000baseT_Full;
reg = efx_mdio_read(efx, MDIO_MMD_AN, MDIO_AN_10GBT_STAT);
if (reg & MDIO_AN_10GBT_STAT_LP10G)
lpa |= ADVERTISED_10000baseT_Full;
mdio45_ethtool_gset_npage(&efx->mdio, ecmd, adv, lpa);
if (efx->phy_type != PHY_TYPE_SFX7101) {
ecmd->supported |= (SUPPORTED_100baseT_Full |
SUPPORTED_1000baseT_Full);
if (ecmd->speed != SPEED_10000) {
ecmd->eth_tp_mdix =
(efx_mdio_read(efx, MDIO_MMD_PMAPMD,
PMA_PMD_XSTATUS_REG) &
(1 << PMA_PMD_XSTAT_MDIX_LBN))
? ETH_TP_MDI_X : ETH_TP_MDI;
}
}
/* In loopback, the PHY automatically brings up the correct interface,
* but doesn't advertise the correct speed. So override it */
if (efx->loopback_mode == LOOPBACK_GPHY)
ecmd->speed = SPEED_1000;
else if (LOOPBACK_MASK(efx) & efx->phy_op->loopbacks)
ecmd->speed = SPEED_10000;
}
static int tenxpress_set_settings(struct efx_nic *efx, struct ethtool_cmd *ecmd)
{
if (!ecmd->autoneg)
return -EINVAL;
return efx_mdio_set_settings(efx, ecmd);
}
static void sfx7101_set_npage_adv(struct efx_nic *efx, u32 advertising)
{
efx_mdio_set_flag(efx, MDIO_MMD_AN, MDIO_AN_10GBT_CTRL,
MDIO_AN_10GBT_CTRL_ADV10G,
advertising & ADVERTISED_10000baseT_Full);
}
static void sft9001_set_npage_adv(struct efx_nic *efx, u32 advertising)
{
efx_mdio_set_flag(efx, MDIO_MMD_C22EXT, C22EXT_MSTSLV_CTRL,
1 << C22EXT_MSTSLV_CTRL_ADV_1000_FD_LBN,
advertising & ADVERTISED_1000baseT_Full);
efx_mdio_set_flag(efx, MDIO_MMD_AN, MDIO_AN_10GBT_CTRL,
MDIO_AN_10GBT_CTRL_ADV10G,
advertising & ADVERTISED_10000baseT_Full);
}
struct efx_phy_operations falcon_sfx7101_phy_ops = {
.macs = EFX_XMAC,
.init = tenxpress_phy_init,
.reconfigure = tenxpress_phy_reconfigure,
.poll = tenxpress_phy_poll,
.fini = tenxpress_phy_fini,
.clear_interrupt = efx_port_dummy_op_void,
.get_settings = tenxpress_get_settings,
.set_settings = tenxpress_set_settings,
.set_npage_adv = sfx7101_set_npage_adv,
.num_tests = ARRAY_SIZE(sfx7101_test_names),
.test_names = sfx7101_test_names,
.run_tests = sfx7101_run_tests,
.mmds = TENXPRESS_REQUIRED_DEVS,
.loopbacks = SFX7101_LOOPBACKS,
};
struct efx_phy_operations falcon_sft9001_phy_ops = {
.macs = EFX_GMAC | EFX_XMAC,
.init = tenxpress_phy_init,
.reconfigure = tenxpress_phy_reconfigure,
.poll = tenxpress_phy_poll,
.fini = tenxpress_phy_fini,
.clear_interrupt = efx_port_dummy_op_void,
.get_settings = tenxpress_get_settings,
.set_settings = tenxpress_set_settings,
.set_npage_adv = sft9001_set_npage_adv,
.num_tests = ARRAY_SIZE(sft9001_test_names),
.test_names = sft9001_test_names,
.run_tests = sft9001_run_tests,
.mmds = TENXPRESS_REQUIRED_DEVS,
.loopbacks = SFT9001_LOOPBACKS,
};

1145
kernel/drivers/net/sfc/tx.c Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,25 @@
/****************************************************************************
* 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);
void efx_init_tx_queue(struct efx_tx_queue *tx_queue);
void efx_fini_tx_queue(struct efx_tx_queue *tx_queue);
netdev_tx_t 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 */

View File

@@ -0,0 +1,62 @@
/****************************************************************************
* 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)
#define EFX_WORKAROUND_10G(efx) EFX_IS10G(efx)
#define EFX_WORKAROUND_SFT9001(efx) ((efx)->phy_type == PHY_TYPE_SFT9001A || \
(efx)->phy_type == PHY_TYPE_SFT9001B)
/* XAUI resets if link not detected */
#define EFX_WORKAROUND_5147 EFX_WORKAROUND_ALWAYS
/* RX PCIe double split performance issue */
#define EFX_WORKAROUND_7575 EFX_WORKAROUND_ALWAYS
/* Bit-bashed I2C reads cause performance drop */
#define EFX_WORKAROUND_7884 EFX_WORKAROUND_10G
/* TX pkt parser problem with <= 16 byte TXes */
#define EFX_WORKAROUND_9141 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
/* Truncated IPv4 packets can confuse the TX packet parser */
#define EFX_WORKAROUND_15592 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
/* Need to send XNP pages for 100BaseT */
#define EFX_WORKAROUND_13204 EFX_WORKAROUND_SFT9001
/* Don't restart AN in near-side loopback */
#define EFX_WORKAROUND_15195 EFX_WORKAROUND_SFT9001
#endif /* EFX_WORKAROUNDS_H */

View File

@@ -0,0 +1,250 @@
/****************************************************************************
* 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 SFP+ and XFP optical PHYs plus some support specific to the
* AMCC QT20xx adapters; see www.amcc.com for details
*/
#include <linux/timer.h>
#include <linux/delay.h>
#include "efx.h"
#include "mdio_10g.h"
#include "phy.h"
#include "falcon.h"
#define XFP_REQUIRED_DEVS (MDIO_DEVS_PCS | \
MDIO_DEVS_PMAPMD | \
MDIO_DEVS_PHYXS)
#define XFP_LOOPBACKS ((1 << LOOPBACK_PCS) | \
(1 << LOOPBACK_PMAPMD) | \
(1 << LOOPBACK_NETWORK))
/****************************************************************************/
/* Quake-specific MDIO registers */
#define MDIO_QUAKE_LED0_REG (0xD006)
/* QT2025C only */
#define PCS_FW_HEARTBEAT_REG 0xd7ee
#define PCS_FW_HEARTB_LBN 0
#define PCS_FW_HEARTB_WIDTH 8
#define PCS_UC8051_STATUS_REG 0xd7fd
#define PCS_UC_STATUS_LBN 0
#define PCS_UC_STATUS_WIDTH 8
#define PCS_UC_STATUS_FW_SAVE 0x20
#define PMA_PMD_FTX_CTRL2_REG 0xc309
#define PMA_PMD_FTX_STATIC_LBN 13
#define PMA_PMD_VEND1_REG 0xc001
#define PMA_PMD_VEND1_LBTXD_LBN 15
#define PCS_VEND1_REG 0xc000
#define PCS_VEND1_LBTXD_LBN 5
void xfp_set_led(struct efx_nic *p, int led, int mode)
{
int addr = MDIO_QUAKE_LED0_REG + led;
efx_mdio_write(p, MDIO_MMD_PMAPMD, addr, mode);
}
struct xfp_phy_data {
enum efx_phy_mode phy_mode;
};
#define XFP_MAX_RESET_TIME 500
#define XFP_RESET_WAIT 10
static int qt2025c_wait_reset(struct efx_nic *efx)
{
unsigned long timeout = jiffies + 10 * HZ;
int reg, old_counter = 0;
/* Wait for firmware heartbeat to start */
for (;;) {
int counter;
reg = efx_mdio_read(efx, MDIO_MMD_PCS, PCS_FW_HEARTBEAT_REG);
if (reg < 0)
return reg;
counter = ((reg >> PCS_FW_HEARTB_LBN) &
((1 << PCS_FW_HEARTB_WIDTH) - 1));
if (old_counter == 0)
old_counter = counter;
else if (counter != old_counter)
break;
if (time_after(jiffies, timeout))
return -ETIMEDOUT;
msleep(10);
}
/* Wait for firmware status to look good */
for (;;) {
reg = efx_mdio_read(efx, MDIO_MMD_PCS, PCS_UC8051_STATUS_REG);
if (reg < 0)
return reg;
if ((reg &
((1 << PCS_UC_STATUS_WIDTH) - 1) << PCS_UC_STATUS_LBN) >=
PCS_UC_STATUS_FW_SAVE)
break;
if (time_after(jiffies, timeout))
return -ETIMEDOUT;
msleep(100);
}
return 0;
}
static int xfp_reset_phy(struct efx_nic *efx)
{
int rc;
if (efx->phy_type == PHY_TYPE_QT2025C) {
/* Wait for the reset triggered by falcon_reset_hw()
* to complete */
rc = qt2025c_wait_reset(efx);
if (rc < 0)
goto fail;
} else {
/* Reset the PHYXS MMD. This is documented as doing
* a complete soft reset. */
rc = efx_mdio_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 = efx_mdio_check_mmds(efx, XFP_REQUIRED_DEVS, MDIO_DEVS_PHYXS);
if (rc < 0)
goto fail;
efx->board_info.init_leds(efx);
return rc;
fail:
EFX_ERR(efx, "PHY reset timed out\n");
return rc;
}
static int xfp_phy_init(struct efx_nic *efx)
{
struct xfp_phy_data *phy_data;
u32 devid = efx_mdio_read_id(efx, MDIO_MMD_PHYXS);
int rc;
phy_data = kzalloc(sizeof(struct xfp_phy_data), GFP_KERNEL);
if (!phy_data)
return -ENOMEM;
efx->phy_data = phy_data;
EFX_INFO(efx, "PHY ID reg %x (OUI %06x model %02x revision %x)\n",
devid, efx_mdio_id_oui(devid), efx_mdio_id_model(devid),
efx_mdio_id_rev(devid));
phy_data->phy_mode = efx->phy_mode;
rc = xfp_reset_phy(efx);
EFX_INFO(efx, "PHY init %s.\n",
rc ? "failed" : "successful");
if (rc < 0)
goto fail;
return 0;
fail:
kfree(efx->phy_data);
efx->phy_data = NULL;
return rc;
}
static void xfp_phy_clear_interrupt(struct efx_nic *efx)
{
/* Read to clear link status alarm */
efx_mdio_read(efx, MDIO_MMD_PMAPMD, MDIO_PMA_LASI_STAT);
}
static int xfp_link_ok(struct efx_nic *efx)
{
return efx_mdio_links_ok(efx, XFP_REQUIRED_DEVS);
}
static void xfp_phy_poll(struct efx_nic *efx)
{
int link_up = xfp_link_ok(efx);
/* Simulate a PHY event if link state has changed */
if (link_up != efx->link_up)
falcon_sim_phy_event(efx);
}
static void xfp_phy_reconfigure(struct efx_nic *efx)
{
struct xfp_phy_data *phy_data = efx->phy_data;
if (efx->phy_type == PHY_TYPE_QT2025C) {
/* There are several different register bits which can
* disable TX (and save power) on direct-attach cables
* or optical transceivers, varying somewhat between
* firmware versions. Only 'static mode' appears to
* cover everything. */
mdio_set_flag(
&efx->mdio, efx->mdio.prtad, MDIO_MMD_PMAPMD,
PMA_PMD_FTX_CTRL2_REG, 1 << PMA_PMD_FTX_STATIC_LBN,
efx->phy_mode & PHY_MODE_TX_DISABLED ||
efx->phy_mode & PHY_MODE_LOW_POWER ||
efx->loopback_mode == LOOPBACK_PCS ||
efx->loopback_mode == LOOPBACK_PMAPMD);
} else {
/* Reset the PHY when moving from tx off to tx on */
if (!(efx->phy_mode & PHY_MODE_TX_DISABLED) &&
(phy_data->phy_mode & PHY_MODE_TX_DISABLED))
xfp_reset_phy(efx);
efx_mdio_transmit_disable(efx);
}
efx_mdio_phy_reconfigure(efx);
phy_data->phy_mode = efx->phy_mode;
efx->link_up = xfp_link_ok(efx);
efx->link_speed = 10000;
efx->link_fd = true;
efx->link_fc = efx->wanted_fc;
}
static void xfp_phy_get_settings(struct efx_nic *efx, struct ethtool_cmd *ecmd)
{
mdio45_ethtool_gset(&efx->mdio, ecmd);
}
static void xfp_phy_fini(struct efx_nic *efx)
{
/* Clobber the LED if it was blinking */
efx->board_info.blink(efx, false);
/* Free the context block */
kfree(efx->phy_data);
efx->phy_data = NULL;
}
struct efx_phy_operations falcon_xfp_phy_ops = {
.macs = EFX_XMAC,
.init = xfp_phy_init,
.reconfigure = xfp_phy_reconfigure,
.poll = xfp_phy_poll,
.fini = xfp_phy_fini,
.clear_interrupt = xfp_phy_clear_interrupt,
.get_settings = xfp_phy_get_settings,
.set_settings = efx_mdio_set_settings,
.mmds = XFP_REQUIRED_DEVS,
.loopbacks = XFP_LOOPBACKS,
};