* Diagnostic Monitoring Interface Monitoring (DOM) PATCH 0/5 for net-next-2.6
@ 2009-11-23 22:45 Robert Olsson
2009-11-25 8:18 ` Jeff Kirsher
0 siblings, 1 reply; 7+ messages in thread
From: Robert Olsson @ 2009-11-23 22:45 UTC (permalink / raw)
To: David Miller; +Cc: netdev, robert
Here are basic support to bring Diagnostic Monitoring Interface Monitoring (DOM)
to Linux this is more or less mandatory when building optical networks.
Optical modules as SFP, SFP+, XFP, GBIC etc holds transceiver and link diagnostic
data needed to monitor and troubleshoot optical links, Talks to networks cards
via the I2C-bus (DOM lives in memory page 0xA2).
In essential:
Usage example: ethtool -D eth5
Ext-Calbr: Avr RX-Power: Alarm & Warn: RX_LOS: Wavelength: 1310 nm
Alarms, warnings in beginning of line, Ie. AH = Alarm High, WL == Warn Low etc
Temp: 35.9 C Thresh: Lo: -12.0/-8.0 Hi: 103.0/110.0 C
Vcc: 3.33 V Thresh: Lo: 3.0/3.0 Hi: 3.7/4.0 V
Tx-Bias: 13.4 mA Thresh: Lo: 2.0/4.0 Hi: 70.0/84.0 mA
ALWL TX-pwr: -5.9 dBm ( 0.26 mW) Thresh: Lo: -4.0/-2.0 Hi: 7.0/8.2 dBm
AHWH RX-pwr: -5.0 dBm ( 0.31 mW) Thresh: Lo: -35.2/-28.0 Hi: -8.2/-6.0 dBm
Read more in Documentation/networking/dom.txt
It's tested with the igb driver, there is also a patch for the ixgbe driver but I
haven't any SFP+ with DOM support yet.
There are room for improvements an clean-ups.
Cheers
--ro
^ permalink raw reply [flat|nested] 7+ messages in thread* Re: Diagnostic Monitoring Interface Monitoring (DOM) PATCH 0/5 for net-next-2.6 2009-11-23 22:45 Diagnostic Monitoring Interface Monitoring (DOM) PATCH 0/5 for net-next-2.6 Robert Olsson @ 2009-11-25 8:18 ` Jeff Kirsher 2009-11-25 9:19 ` Jeff Kirsher 2009-11-25 16:21 ` robert 0 siblings, 2 replies; 7+ messages in thread From: Jeff Kirsher @ 2009-11-25 8:18 UTC (permalink / raw) To: Robert Olsson; +Cc: David Miller, netdev On Mon, Nov 23, 2009 at 14:45, Robert Olsson <robert@herjulf.net> wrote: > > Here are basic support to bring Diagnostic Monitoring Interface Monitoring (DOM) > to Linux this is more or less mandatory when building optical networks. > > Optical modules as SFP, SFP+, XFP, GBIC etc holds transceiver and link diagnostic > data needed to monitor and troubleshoot optical links, Talks to networks cards > via the I2C-bus (DOM lives in memory page 0xA2). > > In essential: > > Usage example: ethtool -D eth5 > > Ext-Calbr: Avr RX-Power: Alarm & Warn: RX_LOS: Wavelength: 1310 nm > Alarms, warnings in beginning of line, Ie. AH = Alarm High, WL == Warn Low etc > Temp: 35.9 C Thresh: Lo: -12.0/-8.0 Hi: 103.0/110.0 C > Vcc: 3.33 V Thresh: Lo: 3.0/3.0 Hi: 3.7/4.0 V > Tx-Bias: 13.4 mA Thresh: Lo: 2.0/4.0 Hi: 70.0/84.0 mA > ALWL TX-pwr: -5.9 dBm ( 0.26 mW) Thresh: Lo: -4.0/-2.0 Hi: 7.0/8.2 dBm > AHWH RX-pwr: -5.0 dBm ( 0.31 mW) Thresh: Lo: -35.2/-28.0 Hi: -8.2/-6.0 dBm > > Read more in Documentation/networking/dom.txt > > It's tested with the igb driver, there is also a patch for the ixgbe driver but I > haven't any SFP+ with DOM support yet. > > > There are room for improvements an clean-ups. > > Cheers > --ro > Robert, to help get more eyes on this I have added it to my tree for internal testing and review. -- Cheers, Jeff ^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: Diagnostic Monitoring Interface Monitoring (DOM) PATCH 0/5 for net-next-2.6 2009-11-25 8:18 ` Jeff Kirsher @ 2009-11-25 9:19 ` Jeff Kirsher 2009-11-25 16:22 ` robert 2009-11-25 16:21 ` robert 1 sibling, 1 reply; 7+ messages in thread From: Jeff Kirsher @ 2009-11-25 9:19 UTC (permalink / raw) To: Robert Olsson; +Cc: David Miller, netdev On Wed, Nov 25, 2009 at 00:18, Jeff Kirsher <jeffrey.t.kirsher@intel.com> wrote: > On Mon, Nov 23, 2009 at 14:45, Robert Olsson <robert@herjulf.net> wrote: >> >> Here are basic support to bring Diagnostic Monitoring Interface Monitoring (DOM) >> to Linux this is more or less mandatory when building optical networks. >> >> Optical modules as SFP, SFP+, XFP, GBIC etc holds transceiver and link diagnostic >> data needed to monitor and troubleshoot optical links, Talks to networks cards >> via the I2C-bus (DOM lives in memory page 0xA2). >> >> In essential: >> >> Usage example: ethtool -D eth5 >> >> Ext-Calbr: Avr RX-Power: Alarm & Warn: RX_LOS: Wavelength: 1310 nm >> Alarms, warnings in beginning of line, Ie. AH = Alarm High, WL == Warn Low etc >> Temp: 35.9 C Thresh: Lo: -12.0/-8.0 Hi: 103.0/110.0 C >> Vcc: 3.33 V Thresh: Lo: 3.0/3.0 Hi: 3.7/4.0 V >> Tx-Bias: 13.4 mA Thresh: Lo: 2.0/4.0 Hi: 70.0/84.0 mA >> ALWL TX-pwr: -5.9 dBm ( 0.26 mW) Thresh: Lo: -4.0/-2.0 Hi: 7.0/8.2 dBm >> AHWH RX-pwr: -5.0 dBm ( 0.31 mW) Thresh: Lo: -35.2/-28.0 Hi: -8.2/-6.0 dBm >> >> Read more in Documentation/networking/dom.txt >> >> It's tested with the igb driver, there is also a patch for the ixgbe driver but I >> haven't any SFP+ with DOM support yet. >> >> >> There are room for improvements an clean-ups. >> >> Cheers >> --ro >> > > Robert, to help get more eyes on this I have added it to my tree for > internal testing and review. > FYI- This patch series is riddled with trailing whitespace, when I imported that patches into my tree, I cleaned up all the whitespace errors. After some internal testing and review, I can re-post the entire series which has been cleaned up. -- Cheers, Jeff ^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: Diagnostic Monitoring Interface Monitoring (DOM) PATCH 0/5 for net-next-2.6 2009-11-25 9:19 ` Jeff Kirsher @ 2009-11-25 16:22 ` robert 2009-11-25 20:38 ` Joe Perches 0 siblings, 1 reply; 7+ messages in thread From: robert @ 2009-11-25 16:22 UTC (permalink / raw) To: Jeff Kirsher; +Cc: Robert Olsson, David Miller, netdev Jeff Kirsher writes: > FYI- This patch series is riddled with trailing whitespace, when I > imported that patches into my tree, I cleaned up all the whitespace > errors. After some internal testing and review, > I can re-post the entire series which has been cleaned up. Please do. Cheers --ro ^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: Diagnostic Monitoring Interface Monitoring (DOM) PATCH 0/5 for net-next-2.6 2009-11-25 16:22 ` robert @ 2009-11-25 20:38 ` Joe Perches 2009-11-26 15:11 ` robert 0 siblings, 1 reply; 7+ messages in thread From: Joe Perches @ 2009-11-25 20:38 UTC (permalink / raw) To: robert; +Cc: Jeff Kirsher, David Miller, netdev On Wed, 2009-11-25 at 17:22 +0100, robert@herjulf.net wrote: > Jeff Kirsher writes: > > FYI- This patch series is riddled with trailing whitespace, when I > > imported that patches into my tree, I cleaned up all the whitespace > > errors. After some internal testing and review, > > I can re-post the entire series which has been cleaned up. > Please do. Here's another attempt as a complete patch. Differences from Robert's original: Whitespace cleanups Changed presumed coding error from: if (type & DOM_TYPE_DOM & DOM_TYPE_ADDR_CHNGE) { to: if (type & DOM_TYPE_ADDR_CHNGE) { Added read_phy_diag_u32 function Refactored multiple sequential register reads to for loop over struct array with register read Functional difference from my original patch: Fixed ARRAY_SIZE(basic) misuse Reduces code size: was: $ size drivers/net/igb/igb_ethtool.o drivers/net/ixgbe/ixgbe_ethtool.o text data bss dec hex filename 26466 1544 8584 36594 8ef2 drivers/net/igb/igb_ethtool.o 31726 2980 9128 43834 ab3a drivers/net/ixgbe/ixgbe_ethtool.o is: $ size drivers/net/igb/igb_ethtool.o drivers/net/ixgbe/ixgbe_ethtool.o text data bss dec hex filename 24642 1544 8008 34194 8592 drivers/net/igb/igb_ethtool.o 29922 2980 8560 41462 a1f6 drivers/net/ixgbe/ixgbe_ethtool.o Signed-off-by: Joe Perches <joe@perches.com> Documentation/networking/dom.txt | 41 ++++++++ drivers/net/igb/igb_ethtool.c | 191 +++++++++++++++++++++++++++++++++++++ drivers/net/ixgbe/ixgbe_ethtool.c | 171 +++++++++++++++++++++++++++++++++- include/linux/ethtool.h | 49 ++++++++++ include/net/dom.h | 109 +++++++++++++++++++++ net/core/ethtool.c | 18 ++++ 6 files changed, 578 insertions(+), 1 deletions(-) diff --git a/Documentation/networking/dom.txt b/Documentation/networking/dom.txt new file mode 100644 index 0000000..4bf24de --- /dev/null +++ b/Documentation/networking/dom.txt @@ -0,0 +1,41 @@ +Diagnostic Monitoring Interface Monitoring also called DOM is a specification +for optical transceivers to allow link and other diagnostics related to the +transceiver's to standardized and communicated. For communication the I2C +bus is used. The SFF specifications is available at ftp://ftp.seagate.com/ +Specification is generic and should work for many types of optical modules +as GBIC, SFP, SFp+, XFP etc + +In short DOM spec adds a memory page where the diagnostics's is kept (address +0xA2 bytes 66 to 105) but there are lot's of option's and variants. For example +alarm and warnings are optional. See example below. + +Not all SFP's (SFP is for GIGE) have DOM support normally long range supports +DOM. And of course your board and driver needs this support too. +For SFP+ (10G) DOM is mandatory. + +Linux kernel support is via ethertool. + +include/net/dom.h # Basic definitions +net/core/ethtool.c # adds ethtool_phy_diag() +include/linux/ethtool.h # adds ETHTOOL_GPHYDIAG + +And drivers hooks exists currently in igb and ixgbe driver + +Usage example: ethtool -D eth5 + +Ext-Calbr: Avr RX-Power: Alarm & Warn: RX_LOS: Wavelength: 1310 nm +Alarms, warnings in beginning of line, Ie. AH = Alarm High, WL == Warn Low etc + Temp: 35.9 C Thresh: Lo: -12.0/-8.0 Hi: 103.0/110.0 C + Vcc: 3.33 V Thresh: Lo: 3.0/3.0 Hi: 3.7/4.0 V + Tx-Bias: 13.4 mA Thresh: Lo: 2.0/4.0 Hi: 70.0/84.0 mA +ALWL TX-pwr: -5.9 dBm ( 0.26 mW) Thresh: Lo: -4.0/-2.0 Hi: 7.0/8.2 dBm +AHWH RX-pwr: -5.0 dBm ( 0.31 mW) Thresh: Lo: -35.2/-28.0 Hi: -8.2/-6.0 dBm + + +First line shows the options supported by the module. As we see this module +supports Alarms and warnings as a consequence thresholds are printed. As example +RX-pwr: -35.2 < no_alarm < -6.0 dBm and -28.0 < no_warning < -8.2 dBm. (dBm yields +1 mW == 0 dBm) + +In the example above we see both warnings for both TX-pwr (low) and RX-Pwr high. +The Rx side would need some attenuation. diff --git a/drivers/net/igb/igb_ethtool.c b/drivers/net/igb/igb_ethtool.c index ac9d527..63a297d 100644 --- a/drivers/net/igb/igb_ethtool.c +++ b/drivers/net/igb/igb_ethtool.c @@ -34,6 +34,7 @@ #include <linux/interrupt.h> #include <linux/if_ether.h> #include <linux/ethtool.h> +#include <net/dom.h> #include <linux/sched.h> #include "igb.h" @@ -2063,6 +2064,195 @@ static void igb_get_strings(struct net_device *netdev, u32 stringset, u8 *data) } } +static s32 read_phy_diag(struct e1000_hw *hw, u8 page, u8 offset, u16 *data) +{ + u32 i, i2ccmd = 0; + + if (offset > E1000_MAX_SGMII_PHY_REG_ADDR) { + hw_dbg("DOM Register Address %u is out of range\n", offset); + return -E1000_ERR_PARAM; + } + + /* + * Set up Op-code, Phy Address, and register address in the I2CCMD + * register. The MAC will take care of interfacing with the + * PHY to retrieve the desired data. + */ + + i2ccmd = (E1000_I2CCMD_OPCODE_READ) | + (page << E1000_I2CCMD_PHY_ADDR_SHIFT) | + (offset << E1000_I2CCMD_REG_ADDR_SHIFT); + + wr32(E1000_I2CCMD, i2ccmd); + + /* Poll the ready bit to see if the I2C read completed */ + for (i = 0; i < E1000_I2CCMD_PHY_TIMEOUT; i++) { + udelay(50); + i2ccmd = rd32(E1000_I2CCMD); + //printk("DATA i2ccmd=0x%x\n", i2ccmd); + if (i2ccmd & E1000_I2CCMD_READY) + break; + } + if (!(i2ccmd & E1000_I2CCMD_READY)) { + hw_dbg("I2CCMD Read did not complete\n"); + return -E1000_ERR_PHY; + } + if (i2ccmd & E1000_I2CCMD_ERROR) { + hw_dbg("I2CCMD Error bit set\n"); + return -E1000_ERR_PHY; + } + + /* Need to byte-swap the 16-bit value. */ + *data = ((i2ccmd >> 8) & 0x00FF) | ((i2ccmd << 8) & 0xFF00); + return 0; +} + +static s32 read_phy_diag_u32(struct e1000_hw *hw, u8 page, u8 offset, u32 *data) +{ + u16 p1; + u16 p2; + s32 res; + + res = read_phy_diag(hw, page, offset, &p1); + if (res) + goto out; + + res = read_phy_diag(hw, page, offset + 2, &p2); + if (res) + goto out; + + *data = ((u32)p1) << 16 | p2; + +out: + return res; +} + +struct reg_offset { + int reg; + size_t offset; +}; + +#define REG_OFFSET(a, b) \ + { .reg = a, .offset = offsetof(struct ethtool_phy_diag, b) } + +int igb_get_phy_diag(struct net_device *netdev, struct ethtool_phy_diag *pd) +{ + struct igb_adapter *adapter = netdev_priv(netdev); + struct e1000_hw *hw = &adapter->hw; + int res; + u8 type, eo; + int i; + + static const struct reg_offset basic[] = { + REG_OFFSET(DOM_A2_TEMP, temp), + REG_OFFSET(DOM_A2_TEMP_SLOPE, temp_slope), + REG_OFFSET(DOM_A2_TEMP_OFFSET, temp_offset), + REG_OFFSET(DOM_A2_VCC, vcc), + REG_OFFSET(DOM_A2_VCC_SLOPE, vcc_slope), + REG_OFFSET(DOM_A2_VCC_OFFSET, vcc_offset), + REG_OFFSET(DOM_A2_TX_BIAS, tx_bias), + REG_OFFSET(DOM_A2_TX_I_SLOPE, tx_bias_slope), + REG_OFFSET(DOM_A2_TX_I_OFFSET, tx_bias_offset), + REG_OFFSET(DOM_A2_TX_PWR, tx_pwr), + REG_OFFSET(DOM_A2_TX_PWR_SLOPE, tx_pwr_slope), + REG_OFFSET(DOM_A2_TX_PWR_OFFSET, tx_pwr_offset), + REG_OFFSET(DOM_A2_RX_PWR, rx_pwr), + }; + + static const struct reg_offset power[] = { + REG_OFFSET(DOM_A2_RX_PWR_0, rx_pwr_cal[0]), + REG_OFFSET(DOM_A2_RX_PWR_1, rx_pwr_cal[1]), + REG_OFFSET(DOM_A2_RX_PWR_2, rx_pwr_cal[2]), + REG_OFFSET(DOM_A2_RX_PWR_3, rx_pwr_cal[3]), + REG_OFFSET(DOM_A2_RX_PWR_4, rx_pwr_cal[4]), + }; + + static const struct reg_offset aw[] = { + REG_OFFSET(DOM_A2_TEMP_AHT, temp_aht), + REG_OFFSET(DOM_A2_TEMP_ALT, temp_alt), + REG_OFFSET(DOM_A2_TEMP_WHT, temp_wht), + REG_OFFSET(DOM_A2_TEMP_WLT, temp_wlt), + REG_OFFSET(DOM_A2_VCC_AHT, vcc_aht), + REG_OFFSET(DOM_A2_VCC_ALT, vcc_alt), + REG_OFFSET(DOM_A2_VCC_WHT, vcc_wht), + REG_OFFSET(DOM_A2_VCC_WLT, vcc_wlt), + REG_OFFSET(DOM_A2_TX_BIAS_AHT, tx_bias_aht), + REG_OFFSET(DOM_A2_TX_BIAS_ALT, tx_bias_alt), + REG_OFFSET(DOM_A2_TX_BIAS_WHT, tx_bias_wht), + REG_OFFSET(DOM_A2_TX_BIAS_WLT, tx_bias_wlt), + REG_OFFSET(DOM_A2_TX_PWR_AHT, tx_pwr_aht), + REG_OFFSET(DOM_A2_TX_PWR_ALT, tx_pwr_alt), + REG_OFFSET(DOM_A2_TX_PWR_WHT, tx_pwr_wht), + REG_OFFSET(DOM_A2_TX_PWR_WLT, tx_pwr_wlt), + REG_OFFSET(DOM_A2_RX_PWR_AHT, rx_pwr_aht), + REG_OFFSET(DOM_A2_RX_PWR_ALT, rx_pwr_alt), + REG_OFFSET(DOM_A2_RX_PWR_WHT, rx_pwr_wht), + REG_OFFSET(DOM_A2_RX_PWR_WLT, rx_pwr_wlt), + }; + + res = read_phy_diag(hw, 0x0, DOM_A0_DOM_TYPE, &pd->type); + if (res) + goto out; + + type = pd->type >> 8; + + if ((~type & DOM_TYPE_DOM) || (type & DOM_TYPE_LEGAGY_DOM)) + goto out; + + if (type & DOM_TYPE_ADDR_CHNGE) { + hw_dbg("DOM module not supported (Address change)\n"); + goto out; + } + + eo = pd->type & 0xFF; + + res = read_phy_diag(hw, 0x0, DOM_A0_WAVELENGTH, &pd->wavelength); + if (res) + goto out; + + /* If supported. Read alarms and Warnings first*/ + if (eo & DOM_EO_AW) { + res = read_phy_diag(hw, 0x1, DOM_A2_ALARM, &pd->alarm); + if (res) + goto out; + res = read_phy_diag(hw, 0x1, DOM_A2_WARNING, &pd->warning); + if (res) + goto out; + } + + /* Basic diag */ + + for (i = 0; i < ARRAY_SIZE(basic); i++) { + res = read_phy_diag(hw, 0x1, basic[i].reg, + (u16 *)((char *)pd + basic[i].offset)); + if (res) + goto out; + } + + /* Power */ + + for (i = 0; i < ARRAY_SIZE(power); i++) { + res = read_phy_diag_u32(hw, 0x1, power[i].reg, + (u32 *)((char *)pd + power[i].offset)); + if (res) + goto out; + } + + /* Thresholds for Alarms and Warnings */ + + if (eo & DOM_EO_AW) { + for (i = 0; i < ARRAY_SIZE(aw); i++) { + res = read_phy_diag(hw, 0x1, aw[i].reg, + (u16 *)((char *)pd + aw[i].offset)); + if (res) + goto out; + } + } + +out: + return res; +} + static const struct ethtool_ops igb_ethtool_ops = { .get_settings = igb_get_settings, .set_settings = igb_set_settings, @@ -2097,6 +2287,7 @@ static const struct ethtool_ops igb_ethtool_ops = { .get_ethtool_stats = igb_get_ethtool_stats, .get_coalesce = igb_get_coalesce, .set_coalesce = igb_set_coalesce, + .get_phy_diag = igb_get_phy_diag, }; void igb_set_ethtool_ops(struct net_device *netdev) diff --git a/drivers/net/ixgbe/ixgbe_ethtool.c b/drivers/net/ixgbe/ixgbe_ethtool.c index 84ab4db..8435cd7 100644 --- a/drivers/net/ixgbe/ixgbe_ethtool.c +++ b/drivers/net/ixgbe/ixgbe_ethtool.c @@ -34,9 +34,11 @@ #include <linux/ethtool.h> #include <linux/vmalloc.h> #include <linux/uaccess.h> +#include <net/dom.h> #include "ixgbe.h" - +#include "ixgbe_phy.h" +#include "ixgbe_common.h" #define IXGBE_ALL_RAR_ENTRIES 16 @@ -2106,6 +2108,172 @@ static int ixgbe_set_flags(struct net_device *netdev, u32 data) } +static s32 read_phy_diag(struct ixgbe_hw *hw, u8 page, u8 offset, u16 *data) +{ + s32 status; + u8 hi, lo; + + status = ixgbe_read_i2c_byte_generic(hw, page, offset, &hi); + if (status) + goto out; + + status = ixgbe_read_i2c_byte_generic(hw, page, offset, &lo); + if (status) + goto out; + + *data = (((u16)hi) << 8) | lo; + +out: + return status; +} + +static s32 read_phy_diag_u32(struct ixgbe_hw *hw, u8 page, u8 offset, u32 *data) +{ + u16 p1; + u16 p2; + s32 res; + + res = read_phy_diag(hw, page, offset, &p1); + if (res) + goto out; + + res = read_phy_diag(hw, page, offset + 2, &p2); + if (res) + goto out; + + *data = ((u32)p1) << 16 | p2; + +out: + return res; +} + +struct reg_offset { + int reg; + size_t offset; +}; + +#define REG_OFFSET(a, b) \ + { .reg = a, .offset = offsetof(struct ethtool_phy_diag, b) } + +int ixgb_get_phy_diag(struct net_device *netdev, struct ethtool_phy_diag *pd) +{ + struct ixgbe_adapter *adapter = netdev_priv(netdev); + struct ixgbe_hw *hw = &adapter->hw; + int res; + u8 type, eo; + int i; + + static const struct reg_offset basic[] = { + REG_OFFSET(DOM_A2_TEMP, temp), + REG_OFFSET(DOM_A2_TEMP_SLOPE, temp_slope), + REG_OFFSET(DOM_A2_TEMP_OFFSET, temp_offset), + REG_OFFSET(DOM_A2_VCC, vcc), + REG_OFFSET(DOM_A2_VCC_SLOPE, vcc_slope), + REG_OFFSET(DOM_A2_VCC_OFFSET, vcc_offset), + REG_OFFSET(DOM_A2_TX_BIAS, tx_bias), + REG_OFFSET(DOM_A2_TX_I_SLOPE, tx_bias_slope), + REG_OFFSET(DOM_A2_TX_I_OFFSET, tx_bias_offset), + REG_OFFSET(DOM_A2_TX_PWR, tx_pwr), + REG_OFFSET(DOM_A2_TX_PWR_SLOPE, tx_pwr_slope), + REG_OFFSET(DOM_A2_TX_PWR_OFFSET, tx_pwr_offset), + REG_OFFSET(DOM_A2_RX_PWR, rx_pwr), + }; + + static const struct reg_offset power[] = { + REG_OFFSET(DOM_A2_RX_PWR_0, rx_pwr_cal[0]), + REG_OFFSET(DOM_A2_RX_PWR_1, rx_pwr_cal[1]), + REG_OFFSET(DOM_A2_RX_PWR_2, rx_pwr_cal[2]), + REG_OFFSET(DOM_A2_RX_PWR_3, rx_pwr_cal[3]), + REG_OFFSET(DOM_A2_RX_PWR_4, rx_pwr_cal[4]), + }; + + static const struct reg_offset aw[] = { + REG_OFFSET(DOM_A2_TEMP_AHT, temp_aht), + REG_OFFSET(DOM_A2_TEMP_ALT, temp_alt), + REG_OFFSET(DOM_A2_TEMP_WHT, temp_wht), + REG_OFFSET(DOM_A2_TEMP_WLT, temp_wlt), + REG_OFFSET(DOM_A2_VCC_AHT, vcc_aht), + REG_OFFSET(DOM_A2_VCC_ALT, vcc_alt), + REG_OFFSET(DOM_A2_VCC_WHT, vcc_wht), + REG_OFFSET(DOM_A2_VCC_WLT, vcc_wlt), + REG_OFFSET(DOM_A2_TX_BIAS_AHT, tx_bias_aht), + REG_OFFSET(DOM_A2_TX_BIAS_ALT, tx_bias_alt), + REG_OFFSET(DOM_A2_TX_BIAS_WHT, tx_bias_wht), + REG_OFFSET(DOM_A2_TX_BIAS_WLT, tx_bias_wlt), + REG_OFFSET(DOM_A2_TX_PWR_AHT, tx_pwr_aht), + REG_OFFSET(DOM_A2_TX_PWR_ALT, tx_pwr_alt), + REG_OFFSET(DOM_A2_TX_PWR_WHT, tx_pwr_wht), + REG_OFFSET(DOM_A2_TX_PWR_WLT, tx_pwr_wlt), + REG_OFFSET(DOM_A2_RX_PWR_AHT, rx_pwr_aht), + REG_OFFSET(DOM_A2_RX_PWR_ALT, rx_pwr_alt), + REG_OFFSET(DOM_A2_RX_PWR_WHT, rx_pwr_wht), + REG_OFFSET(DOM_A2_RX_PWR_WLT, rx_pwr_wlt), + }; + + res = read_phy_diag(hw, 0x0, DOM_A0_DOM_TYPE, &pd->type); + if (res) + goto out; + + type = pd->type >> 8; + + if ((~type & DOM_TYPE_DOM) || (type & DOM_TYPE_LEGAGY_DOM)) + goto out; + + if (type & DOM_TYPE_ADDR_CHNGE) { + hw_dbg(hw, "DOM module not supported (Address change)\n"); + goto out; + } + + eo = pd->type & 0xFF; + + res = read_phy_diag(hw, 0x0, DOM_A0_WAVELENGTH, &pd->wavelength); + if (res) + goto out; + + /* If supported. Read alarms and Warnings first*/ + if (eo & DOM_EO_AW) { + res = read_phy_diag(hw, 0x1, DOM_A2_ALARM, &pd->alarm); + if (res) + goto out; + + res = read_phy_diag(hw, 0x1, DOM_A2_WARNING, &pd->warning); + if (res) + goto out; + } + + /* Basic diag */ + + for (i = 0; i < ARRAY_SIZE(basic); i++) { + res = read_phy_diag(hw, 0x1, basic[i].reg, + (u16 *)((char *)pd + basic[i].offset)); + if (res) + goto out; + } + + /* Power */ + + for (i = 0; i < ARRAY_SIZE(power); i++) { + res = read_phy_diag_u32(hw, 0x1, power[i].reg, + (u32 *)((char *)pd + power[i].offset)); + if (res) + goto out; + } + + /* Thresholds for Alarms and Warnings */ + + if (eo & DOM_EO_AW) { + for (i = 0; i < ARRAY_SIZE(aw); i++) { + res = read_phy_diag(hw, 0x1, aw[i].reg, + (u16 *)((char *)pd + aw[i].offset)); + if (res) + goto out; + } + } + +out: + return res; +} + static const struct ethtool_ops ixgbe_ethtool_ops = { .get_settings = ixgbe_get_settings, .set_settings = ixgbe_set_settings, @@ -2141,6 +2309,7 @@ static const struct ethtool_ops ixgbe_ethtool_ops = { .set_coalesce = ixgbe_set_coalesce, .get_flags = ethtool_op_get_flags, .set_flags = ixgbe_set_flags, + .get_phy_diag = ixgb_get_phy_diag, }; void ixgbe_set_ethtool_ops(struct net_device *netdev) diff --git a/include/linux/ethtool.h b/include/linux/ethtool.h index edd03b7..4e45a94 100644 --- a/include/linux/ethtool.h +++ b/include/linux/ethtool.h @@ -272,6 +272,53 @@ struct ethtool_stats { __u64 data[0]; }; +/* Diagmostic Monitoring Interface Data -- DOM */ +struct ethtool_phy_diag { + __u32 cmd; + /* A0 page */ + __u16 type; + __u16 wavelength; + /* A2 page */ + __u16 alarm; + __u16 warning; + __s16 temp; + __u16 temp_slope; + __s16 temp_offset; + __u16 vcc; + __u16 vcc_slope; + __s16 vcc_offset; + __u16 tx_bias; + __u16 tx_bias_slope; + __s16 tx_bias_offset; + __u16 tx_pwr; + __u16 tx_pwr_slope; + __s16 tx_pwr_offset; + __u16 rx_pwr; + __u32 rx_pwr_cal[5]; + + /* Thresholds */ + __s16 temp_alt; + __s16 temp_aht; + __s16 temp_wlt; + __s16 temp_wht; + __u16 vcc_alt; + __u16 vcc_aht; + __u16 vcc_wlt; + __u16 vcc_wht; + __u16 tx_bias_alt; + __u16 tx_bias_aht; + __u16 tx_bias_wlt; + __u16 tx_bias_wht; + __u16 tx_pwr_alt; + __u16 tx_pwr_aht; + __u16 tx_pwr_wlt; + __u16 tx_pwr_wht; + __u16 rx_pwr_alt; + __u16 rx_pwr_aht; + __u16 rx_pwr_wlt; + __u16 rx_pwr_wht; +}; + struct ethtool_perm_addr { __u32 cmd; /* ETHTOOL_GPERMADDR */ __u32 size; @@ -499,6 +546,7 @@ struct ethtool_ops { int (*set_rxnfc)(struct net_device *, struct ethtool_rxnfc *); int (*flash_device)(struct net_device *, struct ethtool_flash *); int (*reset)(struct net_device *, u32 *); + int (*get_phy_diag)(struct net_device *, struct ethtool_phy_diag *); }; #endif /* __KERNEL__ */ @@ -557,6 +605,7 @@ struct ethtool_ops { #define ETHTOOL_SRXCLSRLINS 0x00000032 /* Insert RX classification rule */ #define ETHTOOL_FLASHDEV 0x00000033 /* Flash firmware to device */ #define ETHTOOL_RESET 0x00000034 /* Reset hardware */ +#define ETHTOOL_GPHYDIAG 0x00000035 /* Get PHY diagnostics */ /* compatibility with older code */ #define SPARC_ETH_GSET ETHTOOL_GSET diff --git a/include/net/dom.h b/include/net/dom.h new file mode 100644 index 0000000..61540c3 --- /dev/null +++ b/include/net/dom.h @@ -0,0 +1,109 @@ +#ifndef _LINUX_DOM_H +#define _LINUX_DOM_H + +/* + Diagnostic Monitoring Interface for Optical Tranceivers + SFF-8472 v. 10.4 (Jan 2009) + ftp://ftp.seagate.com/sff/SFF-8472.PDF + + Licensce GPL. Copyright Robert Olsson robert@herjulf.net +*/ + +#define DOM_A0_IDENTIFIER 0 +#define DOM_A0_WAVELENGTH 60 +#define DOM_A0_CC_BASE 63 +#define DOM_A0_DOM_TYPE 92 + +/* DOM_TYPE codings in DOM_A0_DOM_TYPE */ +#define DOM_TYPE_LEGAGY_DOM (1<<7) +#define DOM_TYPE_DOM (1<<6) /* Has DOM support */ +#define DOM_TYPE_INT_CAL (1<<5) /* Internally calibrated */ +#define DOM_TYPE_EXT_CAL (1<<4) /* Externally calibrated */ +#define DOM_TYPE_RX_PWR (1<<3) /* Received Power OMA=0, 1=average */ +#define DOM_TYPE_ADDR_CHNGE (1<<2) /* Address change required */ + +#define DOM_A0_EO 93 /* Enhanced options */ +#define DOM_EO_AW (1<<7) /* Alarm & Warnings */ +#define DOM_EO_TX_DISABLE (1<<6) +#define DOM_EO_TX_FAULT (1<<5) +#define DOM_EO_RX_LOS (1<<4) +#define DOM_EO_RATE_SELECT_MON (1<<3) +#define DOM_EO_APP_SELECT (1<<2) +#define DOM_EO_RATE_SELECT (1<<1) + +#define DOM_A0_CC_EXT 95 + +#define DOM_A2_TEMP_AHT 0 /* Temp alarm high threshold */ +#define DOM_A2_TEMP_ALT 2 +#define DOM_A2_TEMP_WHT 4 /* Temp warning high threshold */ +#define DOM_A2_TEMP_WLT 6 + +#define DOM_A2_VCC_AHT 8 /* VCC alarm high threshold */ +#define DOM_A2_VCC_ALT 10 +#define DOM_A2_VCC_WHT 12 /* VCC warning high threshold */ +#define DOM_A2_VCC_WLT 14 + +#define DOM_A2_TX_BIAS_AHT 16 /* TX_BIAS alarm high threshold */ +#define DOM_A2_TX_BIAS_ALT 18 +#define DOM_A2_TX_BIAS_WHT 20 /* TX_BIAS warning high threshold */ +#define DOM_A2_TX_BIAS_WLT 22 + +#define DOM_A2_TX_PWR_AHT 24 /* TX_PWR alarm high threshold */ +#define DOM_A2_TX_PWR_ALT 26 +#define DOM_A2_TX_PWR_WHT 28 /* TX_PWR warning high threshold */ +#define DOM_A2_TX_PWR_WLT 30 + +#define DOM_A2_RX_PWR_AHT 32 /* RX_PWR alarm high threshold */ +#define DOM_A2_RX_PWR_ALT 34 +#define DOM_A2_RX_PWR_WHT 36 /* RX_PWR warning high threshold */ +#define DOM_A2_RX_PWR_WLT 38 + +#define DOM_A2_RX_PWR_4 56 /* 4 bytes Calibration constants*/ +#define DOM_A2_RX_PWR_3 60 /* 4 bytes */ +#define DOM_A2_RX_PWR_2 64 /* 4 bytes */ +#define DOM_A2_RX_PWR_1 68 /* 4 bytes */ +#define DOM_A2_RX_PWR_0 72 /* 4 bytes */ + +#define DOM_A2_TX_I_SLOPE 76 /* 2 bytes */ +#define DOM_A2_TX_I_OFFSET 78 /* 2 bytes */ +#define DOM_A2_TX_PWR_SLOPE 80 /* 2 bytes */ +#define DOM_A2_TX_PWR_OFFSET 82 /* 2 bytes */ +#define DOM_A2_TEMP_SLOPE 84 /* 2 bytes */ +#define DOM_A2_TEMP_OFFSET 86 /* 2 bytes */ +#define DOM_A2_VCC_SLOPE 88 /* 2 bytes */ +#define DOM_A2_VCC_OFFSET 90 /* 2 bytes */ + +#define DOM_A2_CC_DMI 95 +#define DOM_A2_TEMP 96 /* 2 bytes */ +#define DOM_A2_VCC 98 /* 2 bytes */ +#define DOM_A2_TX_BIAS 100 /* 2 bytes */ +#define DOM_A2_TX_PWR 102 /* 2 bytes */ +#define DOM_A2_RX_PWR 104 /* 2 bytes */ + +#define DOM_A2_ALARM 112 /* 2 bytes */ +#define DOM_TYPE_TEMP_AH (1<<7) /* Temp alarm high */ +#define DOM_TYPE_TEMP_AL (1<<6) /* low */ +#define DOM_TYPE_VCC_AH (1<<5) +#define DOM_TYPE_VCC_AL (1<<4) +#define DOM_TYPE_TX_BIAS_AH (1<<3) +#define DOM_TYPE_TX_BIAS_AL (1<<2) +#define DOM_TYPE_TX_PWR_AH (1<<1) +#define DOM_TYPE_TX_PWR_AL (1<<0) +/* Next byte 113 */ +#define DOM_TYPE_RX_PWR_AH (1<<7) +#define DOM_TYPE_RX_PWR_AL (1<<6) + +#define DOM_A2_WARNING 116 /* 2 bytes */ +#define DOM_TYPE_TEMP_WH (1<<7) /* Temp warning high */ +#define DOM_TYPE_TEMP_WL (1<<6) /* low */ +#define DOM_TYPE_VCC_WH (1<<5) +#define DOM_TYPE_VCC_WL (1<<4) +#define DOM_TYPE_TX_BIAS_WH (1<<3) +#define DOM_TYPE_TX_BIAS_WL (1<<2) +#define DOM_TYPE_TX_PWR_WH (1<<1) +#define DOM_TYPE_TX_PWR_WL (1<<0) +/* Next byte 117 */ +#define DOM_TYPE_RX_PWR_WH (1<<7) +#define DOM_TYPE_RX_PWR_WL (1<<6) + +#endif /* _LINUX_DOM_H */ diff --git a/net/core/ethtool.c b/net/core/ethtool.c index d8aee58..756434a 100644 --- a/net/core/ethtool.c +++ b/net/core/ethtool.c @@ -893,6 +893,21 @@ static int ethtool_flash_device(struct net_device *dev, char __user *useraddr) return dev->ethtool_ops->flash_device(dev, &efl); } +static int ethtool_phy_diag(struct net_device *dev, void __user *useraddr) +{ + struct ethtool_phy_diag pd; + + if (!dev->ethtool_ops->get_phy_diag) + return -EOPNOTSUPP; + + dev->ethtool_ops->get_phy_diag(dev, &pd); /* FIXME */ + + if (copy_to_user(useraddr, &pd, sizeof(pd))) + return -EFAULT; + + return 0; +} + /* The main entry point in this file. Called from net/core/dev.c */ int dev_ethtool(struct net *net, struct ifreq *ifr) @@ -1112,6 +1127,9 @@ int dev_ethtool(struct net *net, struct ifreq *ifr) case ETHTOOL_RESET: rc = ethtool_reset(dev, useraddr); break; + case ETHTOOL_GPHYDIAG: + rc = ethtool_phy_diag(dev, useraddr); + break; default: rc = -EOPNOTSUPP; } ^ permalink raw reply related [flat|nested] 7+ messages in thread
* Re: Diagnostic Monitoring Interface Monitoring (DOM) PATCH 0/5 for net-next-2.6 2009-11-25 20:38 ` Joe Perches @ 2009-11-26 15:11 ` robert 0 siblings, 0 replies; 7+ messages in thread From: robert @ 2009-11-26 15:11 UTC (permalink / raw) To: Joe Perches; +Cc: robert, Jeff Kirsher, David Miller, netdev Joe Perches writes: > Here's another attempt as a complete patch. Thanks a lot Joe! Yes much nicer... and gives the same result. Signed-off-by: Robert Olsson <robert.olsson@its.uu.se> Cheers --ro > > Differences from Robert's original: > > Whitespace cleanups > Changed presumed coding error from: > if (type & DOM_TYPE_DOM & DOM_TYPE_ADDR_CHNGE) { > to: > if (type & DOM_TYPE_ADDR_CHNGE) { > Added read_phy_diag_u32 function > Refactored multiple sequential register reads to > for loop over struct array with register read > > Functional difference from my original patch: > Fixed ARRAY_SIZE(basic) misuse > > Reduces code size: > > was: > $ size drivers/net/igb/igb_ethtool.o drivers/net/ixgbe/ixgbe_ethtool.o > text data bss dec hex filename > 26466 1544 8584 36594 8ef2 drivers/net/igb/igb_ethtool.o > 31726 2980 9128 43834 ab3a drivers/net/ixgbe/ixgbe_ethtool.o > > is: > $ size drivers/net/igb/igb_ethtool.o drivers/net/ixgbe/ixgbe_ethtool.o > text data bss dec hex filename > 24642 1544 8008 34194 8592 drivers/net/igb/igb_ethtool.o > 29922 2980 8560 41462 a1f6 drivers/net/ixgbe/ixgbe_ethtool.o > > Signed-off-by: Joe Perches <joe@perches.com> > > Documentation/networking/dom.txt | 41 ++++++++ > drivers/net/igb/igb_ethtool.c | 191 +++++++++++++++++++++++++++++++++++++ > drivers/net/ixgbe/ixgbe_ethtool.c | 171 +++++++++++++++++++++++++++++++++- > include/linux/ethtool.h | 49 ++++++++++ > include/net/dom.h | 109 +++++++++++++++++++++ > net/core/ethtool.c | 18 ++++ > 6 files changed, 578 insertions(+), 1 deletions(-) > > diff --git a/Documentation/networking/dom.txt b/Documentation/networking/dom.txt > new file mode 100644 > index 0000000..4bf24de > --- /dev/null > +++ b/Documentation/networking/dom.txt > @@ -0,0 +1,41 @@ > +Diagnostic Monitoring Interface Monitoring also called DOM is a specification > +for optical transceivers to allow link and other diagnostics related to the > +transceiver's to standardized and communicated. For communication the I2C > +bus is used. The SFF specifications is available at ftp://ftp.seagate.com/ > +Specification is generic and should work for many types of optical modules > +as GBIC, SFP, SFp+, XFP etc > + > +In short DOM spec adds a memory page where the diagnostics's is kept (address > +0xA2 bytes 66 to 105) but there are lot's of option's and variants. For example > +alarm and warnings are optional. See example below. > + > +Not all SFP's (SFP is for GIGE) have DOM support normally long range supports > +DOM. And of course your board and driver needs this support too. > +For SFP+ (10G) DOM is mandatory. > + > +Linux kernel support is via ethertool. > + > +include/net/dom.h # Basic definitions > +net/core/ethtool.c # adds ethtool_phy_diag() > +include/linux/ethtool.h # adds ETHTOOL_GPHYDIAG > + > +And drivers hooks exists currently in igb and ixgbe driver > + > +Usage example: ethtool -D eth5 > + > +Ext-Calbr: Avr RX-Power: Alarm & Warn: RX_LOS: Wavelength: 1310 nm > +Alarms, warnings in beginning of line, Ie. AH = Alarm High, WL == Warn Low etc > + Temp: 35.9 C Thresh: Lo: -12.0/-8.0 Hi: 103.0/110.0 C > + Vcc: 3.33 V Thresh: Lo: 3.0/3.0 Hi: 3.7/4.0 V > + Tx-Bias: 13.4 mA Thresh: Lo: 2.0/4.0 Hi: 70.0/84.0 mA > +ALWL TX-pwr: -5.9 dBm ( 0.26 mW) Thresh: Lo: -4.0/-2.0 Hi: 7.0/8.2 dBm > +AHWH RX-pwr: -5.0 dBm ( 0.31 mW) Thresh: Lo: -35.2/-28.0 Hi: -8.2/-6.0 dBm > + > + > +First line shows the options supported by the module. As we see this module > +supports Alarms and warnings as a consequence thresholds are printed. As example > +RX-pwr: -35.2 < no_alarm < -6.0 dBm and -28.0 < no_warning < -8.2 dBm. (dBm yields > +1 mW == 0 dBm) > + > +In the example above we see both warnings for both TX-pwr (low) and RX-Pwr high. > +The Rx side would need some attenuation. > diff --git a/drivers/net/igb/igb_ethtool.c b/drivers/net/igb/igb_ethtool.c > index ac9d527..63a297d 100644 > --- a/drivers/net/igb/igb_ethtool.c > +++ b/drivers/net/igb/igb_ethtool.c > @@ -34,6 +34,7 @@ > #include <linux/interrupt.h> > #include <linux/if_ether.h> > #include <linux/ethtool.h> > +#include <net/dom.h> > #include <linux/sched.h> > > #include "igb.h" > @@ -2063,6 +2064,195 @@ static void igb_get_strings(struct net_device *netdev, u32 stringset, u8 *data) > } > } > > +static s32 read_phy_diag(struct e1000_hw *hw, u8 page, u8 offset, u16 *data) > +{ > + u32 i, i2ccmd = 0; > + > + if (offset > E1000_MAX_SGMII_PHY_REG_ADDR) { > + hw_dbg("DOM Register Address %u is out of range\n", offset); > + return -E1000_ERR_PARAM; > + } > + > + /* > + * Set up Op-code, Phy Address, and register address in the I2CCMD > + * register. The MAC will take care of interfacing with the > + * PHY to retrieve the desired data. > + */ > + > + i2ccmd = (E1000_I2CCMD_OPCODE_READ) | > + (page << E1000_I2CCMD_PHY_ADDR_SHIFT) | > + (offset << E1000_I2CCMD_REG_ADDR_SHIFT); > + > + wr32(E1000_I2CCMD, i2ccmd); > + > + /* Poll the ready bit to see if the I2C read completed */ > + for (i = 0; i < E1000_I2CCMD_PHY_TIMEOUT; i++) { > + udelay(50); > + i2ccmd = rd32(E1000_I2CCMD); > + //printk("DATA i2ccmd=0x%x\n", i2ccmd); > + if (i2ccmd & E1000_I2CCMD_READY) > + break; > + } > + if (!(i2ccmd & E1000_I2CCMD_READY)) { > + hw_dbg("I2CCMD Read did not complete\n"); > + return -E1000_ERR_PHY; > + } > + if (i2ccmd & E1000_I2CCMD_ERROR) { > + hw_dbg("I2CCMD Error bit set\n"); > + return -E1000_ERR_PHY; > + } > + > + /* Need to byte-swap the 16-bit value. */ > + *data = ((i2ccmd >> 8) & 0x00FF) | ((i2ccmd << 8) & 0xFF00); > + return 0; > +} > + > +static s32 read_phy_diag_u32(struct e1000_hw *hw, u8 page, u8 offset, u32 *data) > +{ > + u16 p1; > + u16 p2; > + s32 res; > + > + res = read_phy_diag(hw, page, offset, &p1); > + if (res) > + goto out; > + > + res = read_phy_diag(hw, page, offset + 2, &p2); > + if (res) > + goto out; > + > + *data = ((u32)p1) << 16 | p2; > + > +out: > + return res; > +} > + > +struct reg_offset { > + int reg; > + size_t offset; > +}; > + > +#define REG_OFFSET(a, b) \ > + { .reg = a, .offset = offsetof(struct ethtool_phy_diag, b) } > + > +int igb_get_phy_diag(struct net_device *netdev, struct ethtool_phy_diag *pd) > +{ > + struct igb_adapter *adapter = netdev_priv(netdev); > + struct e1000_hw *hw = &adapter->hw; > + int res; > + u8 type, eo; > + int i; > + > + static const struct reg_offset basic[] = { > + REG_OFFSET(DOM_A2_TEMP, temp), > + REG_OFFSET(DOM_A2_TEMP_SLOPE, temp_slope), > + REG_OFFSET(DOM_A2_TEMP_OFFSET, temp_offset), > + REG_OFFSET(DOM_A2_VCC, vcc), > + REG_OFFSET(DOM_A2_VCC_SLOPE, vcc_slope), > + REG_OFFSET(DOM_A2_VCC_OFFSET, vcc_offset), > + REG_OFFSET(DOM_A2_TX_BIAS, tx_bias), > + REG_OFFSET(DOM_A2_TX_I_SLOPE, tx_bias_slope), > + REG_OFFSET(DOM_A2_TX_I_OFFSET, tx_bias_offset), > + REG_OFFSET(DOM_A2_TX_PWR, tx_pwr), > + REG_OFFSET(DOM_A2_TX_PWR_SLOPE, tx_pwr_slope), > + REG_OFFSET(DOM_A2_TX_PWR_OFFSET, tx_pwr_offset), > + REG_OFFSET(DOM_A2_RX_PWR, rx_pwr), > + }; > + > + static const struct reg_offset power[] = { > + REG_OFFSET(DOM_A2_RX_PWR_0, rx_pwr_cal[0]), > + REG_OFFSET(DOM_A2_RX_PWR_1, rx_pwr_cal[1]), > + REG_OFFSET(DOM_A2_RX_PWR_2, rx_pwr_cal[2]), > + REG_OFFSET(DOM_A2_RX_PWR_3, rx_pwr_cal[3]), > + REG_OFFSET(DOM_A2_RX_PWR_4, rx_pwr_cal[4]), > + }; > + > + static const struct reg_offset aw[] = { > + REG_OFFSET(DOM_A2_TEMP_AHT, temp_aht), > + REG_OFFSET(DOM_A2_TEMP_ALT, temp_alt), > + REG_OFFSET(DOM_A2_TEMP_WHT, temp_wht), > + REG_OFFSET(DOM_A2_TEMP_WLT, temp_wlt), > + REG_OFFSET(DOM_A2_VCC_AHT, vcc_aht), > + REG_OFFSET(DOM_A2_VCC_ALT, vcc_alt), > + REG_OFFSET(DOM_A2_VCC_WHT, vcc_wht), > + REG_OFFSET(DOM_A2_VCC_WLT, vcc_wlt), > + REG_OFFSET(DOM_A2_TX_BIAS_AHT, tx_bias_aht), > + REG_OFFSET(DOM_A2_TX_BIAS_ALT, tx_bias_alt), > + REG_OFFSET(DOM_A2_TX_BIAS_WHT, tx_bias_wht), > + REG_OFFSET(DOM_A2_TX_BIAS_WLT, tx_bias_wlt), > + REG_OFFSET(DOM_A2_TX_PWR_AHT, tx_pwr_aht), > + REG_OFFSET(DOM_A2_TX_PWR_ALT, tx_pwr_alt), > + REG_OFFSET(DOM_A2_TX_PWR_WHT, tx_pwr_wht), > + REG_OFFSET(DOM_A2_TX_PWR_WLT, tx_pwr_wlt), > + REG_OFFSET(DOM_A2_RX_PWR_AHT, rx_pwr_aht), > + REG_OFFSET(DOM_A2_RX_PWR_ALT, rx_pwr_alt), > + REG_OFFSET(DOM_A2_RX_PWR_WHT, rx_pwr_wht), > + REG_OFFSET(DOM_A2_RX_PWR_WLT, rx_pwr_wlt), > + }; > + > + res = read_phy_diag(hw, 0x0, DOM_A0_DOM_TYPE, &pd->type); > + if (res) > + goto out; > + > + type = pd->type >> 8; > + > + if ((~type & DOM_TYPE_DOM) || (type & DOM_TYPE_LEGAGY_DOM)) > + goto out; > + > + if (type & DOM_TYPE_ADDR_CHNGE) { > + hw_dbg("DOM module not supported (Address change)\n"); > + goto out; > + } > + > + eo = pd->type & 0xFF; > + > + res = read_phy_diag(hw, 0x0, DOM_A0_WAVELENGTH, &pd->wavelength); > + if (res) > + goto out; > + > + /* If supported. Read alarms and Warnings first*/ > + if (eo & DOM_EO_AW) { > + res = read_phy_diag(hw, 0x1, DOM_A2_ALARM, &pd->alarm); > + if (res) > + goto out; > + res = read_phy_diag(hw, 0x1, DOM_A2_WARNING, &pd->warning); > + if (res) > + goto out; > + } > + > + /* Basic diag */ > + > + for (i = 0; i < ARRAY_SIZE(basic); i++) { > + res = read_phy_diag(hw, 0x1, basic[i].reg, > + (u16 *)((char *)pd + basic[i].offset)); > + if (res) > + goto out; > + } > + > + /* Power */ > + > + for (i = 0; i < ARRAY_SIZE(power); i++) { > + res = read_phy_diag_u32(hw, 0x1, power[i].reg, > + (u32 *)((char *)pd + power[i].offset)); > + if (res) > + goto out; > + } > + > + /* Thresholds for Alarms and Warnings */ > + > + if (eo & DOM_EO_AW) { > + for (i = 0; i < ARRAY_SIZE(aw); i++) { > + res = read_phy_diag(hw, 0x1, aw[i].reg, > + (u16 *)((char *)pd + aw[i].offset)); > + if (res) > + goto out; > + } > + } > + > +out: > + return res; > +} > + > static const struct ethtool_ops igb_ethtool_ops = { > .get_settings = igb_get_settings, > .set_settings = igb_set_settings, > @@ -2097,6 +2287,7 @@ static const struct ethtool_ops igb_ethtool_ops = { > .get_ethtool_stats = igb_get_ethtool_stats, > .get_coalesce = igb_get_coalesce, > .set_coalesce = igb_set_coalesce, > + .get_phy_diag = igb_get_phy_diag, > }; > > void igb_set_ethtool_ops(struct net_device *netdev) > diff --git a/drivers/net/ixgbe/ixgbe_ethtool.c b/drivers/net/ixgbe/ixgbe_ethtool.c > index 84ab4db..8435cd7 100644 > --- a/drivers/net/ixgbe/ixgbe_ethtool.c > +++ b/drivers/net/ixgbe/ixgbe_ethtool.c > @@ -34,9 +34,11 @@ > #include <linux/ethtool.h> > #include <linux/vmalloc.h> > #include <linux/uaccess.h> > +#include <net/dom.h> > > #include "ixgbe.h" > - > +#include "ixgbe_phy.h" > +#include "ixgbe_common.h" > > #define IXGBE_ALL_RAR_ENTRIES 16 > > @@ -2106,6 +2108,172 @@ static int ixgbe_set_flags(struct net_device *netdev, u32 data) > > } > > +static s32 read_phy_diag(struct ixgbe_hw *hw, u8 page, u8 offset, u16 *data) > +{ > + s32 status; > + u8 hi, lo; > + > + status = ixgbe_read_i2c_byte_generic(hw, page, offset, &hi); > + if (status) > + goto out; > + > + status = ixgbe_read_i2c_byte_generic(hw, page, offset, &lo); > + if (status) > + goto out; > + > + *data = (((u16)hi) << 8) | lo; > + > +out: > + return status; > +} > + > +static s32 read_phy_diag_u32(struct ixgbe_hw *hw, u8 page, u8 offset, u32 *data) > +{ > + u16 p1; > + u16 p2; > + s32 res; > + > + res = read_phy_diag(hw, page, offset, &p1); > + if (res) > + goto out; > + > + res = read_phy_diag(hw, page, offset + 2, &p2); > + if (res) > + goto out; > + > + *data = ((u32)p1) << 16 | p2; > + > +out: > + return res; > +} > + > +struct reg_offset { > + int reg; > + size_t offset; > +}; > + > +#define REG_OFFSET(a, b) \ > + { .reg = a, .offset = offsetof(struct ethtool_phy_diag, b) } > + > +int ixgb_get_phy_diag(struct net_device *netdev, struct ethtool_phy_diag *pd) > +{ > + struct ixgbe_adapter *adapter = netdev_priv(netdev); > + struct ixgbe_hw *hw = &adapter->hw; > + int res; > + u8 type, eo; > + int i; > + > + static const struct reg_offset basic[] = { > + REG_OFFSET(DOM_A2_TEMP, temp), > + REG_OFFSET(DOM_A2_TEMP_SLOPE, temp_slope), > + REG_OFFSET(DOM_A2_TEMP_OFFSET, temp_offset), > + REG_OFFSET(DOM_A2_VCC, vcc), > + REG_OFFSET(DOM_A2_VCC_SLOPE, vcc_slope), > + REG_OFFSET(DOM_A2_VCC_OFFSET, vcc_offset), > + REG_OFFSET(DOM_A2_TX_BIAS, tx_bias), > + REG_OFFSET(DOM_A2_TX_I_SLOPE, tx_bias_slope), > + REG_OFFSET(DOM_A2_TX_I_OFFSET, tx_bias_offset), > + REG_OFFSET(DOM_A2_TX_PWR, tx_pwr), > + REG_OFFSET(DOM_A2_TX_PWR_SLOPE, tx_pwr_slope), > + REG_OFFSET(DOM_A2_TX_PWR_OFFSET, tx_pwr_offset), > + REG_OFFSET(DOM_A2_RX_PWR, rx_pwr), > + }; > + > + static const struct reg_offset power[] = { > + REG_OFFSET(DOM_A2_RX_PWR_0, rx_pwr_cal[0]), > + REG_OFFSET(DOM_A2_RX_PWR_1, rx_pwr_cal[1]), > + REG_OFFSET(DOM_A2_RX_PWR_2, rx_pwr_cal[2]), > + REG_OFFSET(DOM_A2_RX_PWR_3, rx_pwr_cal[3]), > + REG_OFFSET(DOM_A2_RX_PWR_4, rx_pwr_cal[4]), > + }; > + > + static const struct reg_offset aw[] = { > + REG_OFFSET(DOM_A2_TEMP_AHT, temp_aht), > + REG_OFFSET(DOM_A2_TEMP_ALT, temp_alt), > + REG_OFFSET(DOM_A2_TEMP_WHT, temp_wht), > + REG_OFFSET(DOM_A2_TEMP_WLT, temp_wlt), > + REG_OFFSET(DOM_A2_VCC_AHT, vcc_aht), > + REG_OFFSET(DOM_A2_VCC_ALT, vcc_alt), > + REG_OFFSET(DOM_A2_VCC_WHT, vcc_wht), > + REG_OFFSET(DOM_A2_VCC_WLT, vcc_wlt), > + REG_OFFSET(DOM_A2_TX_BIAS_AHT, tx_bias_aht), > + REG_OFFSET(DOM_A2_TX_BIAS_ALT, tx_bias_alt), > + REG_OFFSET(DOM_A2_TX_BIAS_WHT, tx_bias_wht), > + REG_OFFSET(DOM_A2_TX_BIAS_WLT, tx_bias_wlt), > + REG_OFFSET(DOM_A2_TX_PWR_AHT, tx_pwr_aht), > + REG_OFFSET(DOM_A2_TX_PWR_ALT, tx_pwr_alt), > + REG_OFFSET(DOM_A2_TX_PWR_WHT, tx_pwr_wht), > + REG_OFFSET(DOM_A2_TX_PWR_WLT, tx_pwr_wlt), > + REG_OFFSET(DOM_A2_RX_PWR_AHT, rx_pwr_aht), > + REG_OFFSET(DOM_A2_RX_PWR_ALT, rx_pwr_alt), > + REG_OFFSET(DOM_A2_RX_PWR_WHT, rx_pwr_wht), > + REG_OFFSET(DOM_A2_RX_PWR_WLT, rx_pwr_wlt), > + }; > + > + res = read_phy_diag(hw, 0x0, DOM_A0_DOM_TYPE, &pd->type); > + if (res) > + goto out; > + > + type = pd->type >> 8; > + > + if ((~type & DOM_TYPE_DOM) || (type & DOM_TYPE_LEGAGY_DOM)) > + goto out; > + > + if (type & DOM_TYPE_ADDR_CHNGE) { > + hw_dbg(hw, "DOM module not supported (Address change)\n"); > + goto out; > + } > + > + eo = pd->type & 0xFF; > + > + res = read_phy_diag(hw, 0x0, DOM_A0_WAVELENGTH, &pd->wavelength); > + if (res) > + goto out; > + > + /* If supported. Read alarms and Warnings first*/ > + if (eo & DOM_EO_AW) { > + res = read_phy_diag(hw, 0x1, DOM_A2_ALARM, &pd->alarm); > + if (res) > + goto out; > + > + res = read_phy_diag(hw, 0x1, DOM_A2_WARNING, &pd->warning); > + if (res) > + goto out; > + } > + > + /* Basic diag */ > + > + for (i = 0; i < ARRAY_SIZE(basic); i++) { > + res = read_phy_diag(hw, 0x1, basic[i].reg, > + (u16 *)((char *)pd + basic[i].offset)); > + if (res) > + goto out; > + } > + > + /* Power */ > + > + for (i = 0; i < ARRAY_SIZE(power); i++) { > + res = read_phy_diag_u32(hw, 0x1, power[i].reg, > + (u32 *)((char *)pd + power[i].offset)); > + if (res) > + goto out; > + } > + > + /* Thresholds for Alarms and Warnings */ > + > + if (eo & DOM_EO_AW) { > + for (i = 0; i < ARRAY_SIZE(aw); i++) { > + res = read_phy_diag(hw, 0x1, aw[i].reg, > + (u16 *)((char *)pd + aw[i].offset)); > + if (res) > + goto out; > + } > + } > + > +out: > + return res; > +} > + > static const struct ethtool_ops ixgbe_ethtool_ops = { > .get_settings = ixgbe_get_settings, > .set_settings = ixgbe_set_settings, > @@ -2141,6 +2309,7 @@ static const struct ethtool_ops ixgbe_ethtool_ops = { > .set_coalesce = ixgbe_set_coalesce, > .get_flags = ethtool_op_get_flags, > .set_flags = ixgbe_set_flags, > + .get_phy_diag = ixgb_get_phy_diag, > }; > > void ixgbe_set_ethtool_ops(struct net_device *netdev) > diff --git a/include/linux/ethtool.h b/include/linux/ethtool.h > index edd03b7..4e45a94 100644 > --- a/include/linux/ethtool.h > +++ b/include/linux/ethtool.h > @@ -272,6 +272,53 @@ struct ethtool_stats { > __u64 data[0]; > }; > > +/* Diagmostic Monitoring Interface Data -- DOM */ > +struct ethtool_phy_diag { > + __u32 cmd; > + /* A0 page */ > + __u16 type; > + __u16 wavelength; > + /* A2 page */ > + __u16 alarm; > + __u16 warning; > + __s16 temp; > + __u16 temp_slope; > + __s16 temp_offset; > + __u16 vcc; > + __u16 vcc_slope; > + __s16 vcc_offset; > + __u16 tx_bias; > + __u16 tx_bias_slope; > + __s16 tx_bias_offset; > + __u16 tx_pwr; > + __u16 tx_pwr_slope; > + __s16 tx_pwr_offset; > + __u16 rx_pwr; > + __u32 rx_pwr_cal[5]; > + > + /* Thresholds */ > + __s16 temp_alt; > + __s16 temp_aht; > + __s16 temp_wlt; > + __s16 temp_wht; > + __u16 vcc_alt; > + __u16 vcc_aht; > + __u16 vcc_wlt; > + __u16 vcc_wht; > + __u16 tx_bias_alt; > + __u16 tx_bias_aht; > + __u16 tx_bias_wlt; > + __u16 tx_bias_wht; > + __u16 tx_pwr_alt; > + __u16 tx_pwr_aht; > + __u16 tx_pwr_wlt; > + __u16 tx_pwr_wht; > + __u16 rx_pwr_alt; > + __u16 rx_pwr_aht; > + __u16 rx_pwr_wlt; > + __u16 rx_pwr_wht; > +}; > + > struct ethtool_perm_addr { > __u32 cmd; /* ETHTOOL_GPERMADDR */ > __u32 size; > @@ -499,6 +546,7 @@ struct ethtool_ops { > int (*set_rxnfc)(struct net_device *, struct ethtool_rxnfc *); > int (*flash_device)(struct net_device *, struct ethtool_flash *); > int (*reset)(struct net_device *, u32 *); > + int (*get_phy_diag)(struct net_device *, struct ethtool_phy_diag *); > }; > #endif /* __KERNEL__ */ > > @@ -557,6 +605,7 @@ struct ethtool_ops { > #define ETHTOOL_SRXCLSRLINS 0x00000032 /* Insert RX classification rule */ > #define ETHTOOL_FLASHDEV 0x00000033 /* Flash firmware to device */ > #define ETHTOOL_RESET 0x00000034 /* Reset hardware */ > +#define ETHTOOL_GPHYDIAG 0x00000035 /* Get PHY diagnostics */ > > /* compatibility with older code */ > #define SPARC_ETH_GSET ETHTOOL_GSET > diff --git a/include/net/dom.h b/include/net/dom.h > new file mode 100644 > index 0000000..61540c3 > --- /dev/null > +++ b/include/net/dom.h > @@ -0,0 +1,109 @@ > +#ifndef _LINUX_DOM_H > +#define _LINUX_DOM_H > + > +/* > + Diagnostic Monitoring Interface for Optical Tranceivers > + SFF-8472 v. 10.4 (Jan 2009) > + ftp://ftp.seagate.com/sff/SFF-8472.PDF > + > + Licensce GPL. Copyright Robert Olsson robert@herjulf.net > +*/ > + > +#define DOM_A0_IDENTIFIER 0 > +#define DOM_A0_WAVELENGTH 60 > +#define DOM_A0_CC_BASE 63 > +#define DOM_A0_DOM_TYPE 92 > + > +/* DOM_TYPE codings in DOM_A0_DOM_TYPE */ > +#define DOM_TYPE_LEGAGY_DOM (1<<7) > +#define DOM_TYPE_DOM (1<<6) /* Has DOM support */ > +#define DOM_TYPE_INT_CAL (1<<5) /* Internally calibrated */ > +#define DOM_TYPE_EXT_CAL (1<<4) /* Externally calibrated */ > +#define DOM_TYPE_RX_PWR (1<<3) /* Received Power OMA=0, 1=average */ > +#define DOM_TYPE_ADDR_CHNGE (1<<2) /* Address change required */ > + > +#define DOM_A0_EO 93 /* Enhanced options */ > +#define DOM_EO_AW (1<<7) /* Alarm & Warnings */ > +#define DOM_EO_TX_DISABLE (1<<6) > +#define DOM_EO_TX_FAULT (1<<5) > +#define DOM_EO_RX_LOS (1<<4) > +#define DOM_EO_RATE_SELECT_MON (1<<3) > +#define DOM_EO_APP_SELECT (1<<2) > +#define DOM_EO_RATE_SELECT (1<<1) > + > +#define DOM_A0_CC_EXT 95 > + > +#define DOM_A2_TEMP_AHT 0 /* Temp alarm high threshold */ > +#define DOM_A2_TEMP_ALT 2 > +#define DOM_A2_TEMP_WHT 4 /* Temp warning high threshold */ > +#define DOM_A2_TEMP_WLT 6 > + > +#define DOM_A2_VCC_AHT 8 /* VCC alarm high threshold */ > +#define DOM_A2_VCC_ALT 10 > +#define DOM_A2_VCC_WHT 12 /* VCC warning high threshold */ > +#define DOM_A2_VCC_WLT 14 > + > +#define DOM_A2_TX_BIAS_AHT 16 /* TX_BIAS alarm high threshold */ > +#define DOM_A2_TX_BIAS_ALT 18 > +#define DOM_A2_TX_BIAS_WHT 20 /* TX_BIAS warning high threshold */ > +#define DOM_A2_TX_BIAS_WLT 22 > + > +#define DOM_A2_TX_PWR_AHT 24 /* TX_PWR alarm high threshold */ > +#define DOM_A2_TX_PWR_ALT 26 > +#define DOM_A2_TX_PWR_WHT 28 /* TX_PWR warning high threshold */ > +#define DOM_A2_TX_PWR_WLT 30 > + > +#define DOM_A2_RX_PWR_AHT 32 /* RX_PWR alarm high threshold */ > +#define DOM_A2_RX_PWR_ALT 34 > +#define DOM_A2_RX_PWR_WHT 36 /* RX_PWR warning high threshold */ > +#define DOM_A2_RX_PWR_WLT 38 > + > +#define DOM_A2_RX_PWR_4 56 /* 4 bytes Calibration constants*/ > +#define DOM_A2_RX_PWR_3 60 /* 4 bytes */ > +#define DOM_A2_RX_PWR_2 64 /* 4 bytes */ > +#define DOM_A2_RX_PWR_1 68 /* 4 bytes */ > +#define DOM_A2_RX_PWR_0 72 /* 4 bytes */ > + > +#define DOM_A2_TX_I_SLOPE 76 /* 2 bytes */ > +#define DOM_A2_TX_I_OFFSET 78 /* 2 bytes */ > +#define DOM_A2_TX_PWR_SLOPE 80 /* 2 bytes */ > +#define DOM_A2_TX_PWR_OFFSET 82 /* 2 bytes */ > +#define DOM_A2_TEMP_SLOPE 84 /* 2 bytes */ > +#define DOM_A2_TEMP_OFFSET 86 /* 2 bytes */ > +#define DOM_A2_VCC_SLOPE 88 /* 2 bytes */ > +#define DOM_A2_VCC_OFFSET 90 /* 2 bytes */ > + > +#define DOM_A2_CC_DMI 95 > +#define DOM_A2_TEMP 96 /* 2 bytes */ > +#define DOM_A2_VCC 98 /* 2 bytes */ > +#define DOM_A2_TX_BIAS 100 /* 2 bytes */ > +#define DOM_A2_TX_PWR 102 /* 2 bytes */ > +#define DOM_A2_RX_PWR 104 /* 2 bytes */ > + > +#define DOM_A2_ALARM 112 /* 2 bytes */ > +#define DOM_TYPE_TEMP_AH (1<<7) /* Temp alarm high */ > +#define DOM_TYPE_TEMP_AL (1<<6) /* low */ > +#define DOM_TYPE_VCC_AH (1<<5) > +#define DOM_TYPE_VCC_AL (1<<4) > +#define DOM_TYPE_TX_BIAS_AH (1<<3) > +#define DOM_TYPE_TX_BIAS_AL (1<<2) > +#define DOM_TYPE_TX_PWR_AH (1<<1) > +#define DOM_TYPE_TX_PWR_AL (1<<0) > +/* Next byte 113 */ > +#define DOM_TYPE_RX_PWR_AH (1<<7) > +#define DOM_TYPE_RX_PWR_AL (1<<6) > + > +#define DOM_A2_WARNING 116 /* 2 bytes */ > +#define DOM_TYPE_TEMP_WH (1<<7) /* Temp warning high */ > +#define DOM_TYPE_TEMP_WL (1<<6) /* low */ > +#define DOM_TYPE_VCC_WH (1<<5) > +#define DOM_TYPE_VCC_WL (1<<4) > +#define DOM_TYPE_TX_BIAS_WH (1<<3) > +#define DOM_TYPE_TX_BIAS_WL (1<<2) > +#define DOM_TYPE_TX_PWR_WH (1<<1) > +#define DOM_TYPE_TX_PWR_WL (1<<0) > +/* Next byte 117 */ > +#define DOM_TYPE_RX_PWR_WH (1<<7) > +#define DOM_TYPE_RX_PWR_WL (1<<6) > + > +#endif /* _LINUX_DOM_H */ > diff --git a/net/core/ethtool.c b/net/core/ethtool.c > index d8aee58..756434a 100644 > --- a/net/core/ethtool.c > +++ b/net/core/ethtool.c > @@ -893,6 +893,21 @@ static int ethtool_flash_device(struct net_device *dev, char __user *useraddr) > return dev->ethtool_ops->flash_device(dev, &efl); > } > > +static int ethtool_phy_diag(struct net_device *dev, void __user *useraddr) > +{ > + struct ethtool_phy_diag pd; > + > + if (!dev->ethtool_ops->get_phy_diag) > + return -EOPNOTSUPP; > + > + dev->ethtool_ops->get_phy_diag(dev, &pd); /* FIXME */ > + > + if (copy_to_user(useraddr, &pd, sizeof(pd))) > + return -EFAULT; > + > + return 0; > +} > + > /* The main entry point in this file. Called from net/core/dev.c */ > > int dev_ethtool(struct net *net, struct ifreq *ifr) > @@ -1112,6 +1127,9 @@ int dev_ethtool(struct net *net, struct ifreq *ifr) > case ETHTOOL_RESET: > rc = ethtool_reset(dev, useraddr); > break; > + case ETHTOOL_GPHYDIAG: > + rc = ethtool_phy_diag(dev, useraddr); > + break; > default: > rc = -EOPNOTSUPP; > } > > > -- > To unsubscribe from this list: send the line "unsubscribe netdev" in > the body of a message to majordomo@vger.kernel.org > More majordomo info at http://vger.kernel.org/majordomo-info.html ^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: Diagnostic Monitoring Interface Monitoring (DOM) PATCH 0/5 for net-next-2.6 2009-11-25 8:18 ` Jeff Kirsher 2009-11-25 9:19 ` Jeff Kirsher @ 2009-11-25 16:21 ` robert 1 sibling, 0 replies; 7+ messages in thread From: robert @ 2009-11-25 16:21 UTC (permalink / raw) To: Jeff Kirsher; +Cc: Robert Olsson, David Miller, netdev Jeff Kirsher writes: > Robert, to help get more eyes on this I have added it to my tree for > internal testing and review. Fine. The igb patch was tested with a 4-port 82576 board from Interface Masters but they forgot to mount the pull-up resistors for the I2C-bus on my prototype which took some weeks to debug. Anyway there are working boards now. About the the Intel brand 82599 board with fixed SFP's. I don't see any DOM support from them. It could be idea check if my reading from I2c is correct. Seems I'll have a SFP+ with DOM support soon. Cheers --ro ^ permalink raw reply [flat|nested] 7+ messages in thread
end of thread, other threads:[~2009-11-26 15:11 UTC | newest] Thread overview: 7+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2009-11-23 22:45 Diagnostic Monitoring Interface Monitoring (DOM) PATCH 0/5 for net-next-2.6 Robert Olsson 2009-11-25 8:18 ` Jeff Kirsher 2009-11-25 9:19 ` Jeff Kirsher 2009-11-25 16:22 ` robert 2009-11-25 20:38 ` Joe Perches 2009-11-26 15:11 ` robert 2009-11-25 16:21 ` robert
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox