12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382 |
- From bf0a000960234c0e773fadea47240c3cda0cab02 Mon Sep 17 00:00:00 2001
- From: Russell King <rmk+kernel@arm.linux.org.uk>
- Date: Sat, 12 Sep 2015 18:43:39 +0100
- Subject: [PATCH 720/744] sfp: add phylink based SFP module support
- Add support for SFP hotpluggable modules via phylink. This supports
- both copper and optical SFP modules, which require different Serdes
- modes in order to properly negotiate the link.
- Optical SFP modules typically require the Serdes link to be talking
- 1000base-X mode - this is the gigabit ethernet mode defined by the
- 802.3 standard.
- Copper SFP modules typically integrate a PHY in the module to convert
- from Serdes to copper, and the PHY will be configured by the vendor
- to either present a 1000base-X Serdes link (for fixed 1000base-T) or
- a SGMII Serdes link. However, this is vendor defined, so we instead
- detect the PHY, switch the link to SGMII mode, and use traditional
- PHY based negotiation.
- Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
- ---
- drivers/net/phy/Kconfig | 5 +
- drivers/net/phy/Makefile | 1 +
- drivers/net/phy/sfp.c | 986 +++++++++++++++++++++++++++++++++++++++++++++++
- include/linux/sfp.h | 339 ++++++++++++++++
- 4 files changed, 1331 insertions(+)
- create mode 100644 drivers/net/phy/sfp.c
- create mode 100644 include/linux/sfp.h
- --- a/drivers/net/phy/Kconfig
- +++ b/drivers/net/phy/Kconfig
- @@ -225,6 +225,11 @@ config FIXED_PHY
-
- Currently tested with mpc866ads and mpc8349e-mitx.
-
- +config SFP
- + tristate "SFP cage support"
- + depends on I2C && PHYLINK
- + select MDIO_I2C
- +
- config MDIO_BITBANG
- tristate "Support for bitbanged MDIO buses"
- help
- --- a/drivers/net/phy/Makefile
- +++ b/drivers/net/phy/Makefile
- @@ -61,3 +61,4 @@ obj-$(CONFIG_MDIO_MOXART) += mdio-moxart
- obj-$(CONFIG_MDIO_BCM_UNIMAC) += mdio-bcm-unimac.o
- obj-$(CONFIG_MICROCHIP_PHY) += microchip.o
- obj-$(CONFIG_MDIO_BCM_IPROC) += mdio-bcm-iproc.o
- +obj-$(CONFIG_SFP) += sfp.o
- --- /dev/null
- +++ b/drivers/net/phy/sfp.c
- @@ -0,0 +1,986 @@
- +#include <linux/delay.h>
- +#include <linux/gpio.h>
- +#include <linux/i2c.h>
- +#include <linux/interrupt.h>
- +#include <linux/jiffies.h>
- +#include <linux/module.h>
- +#include <linux/mutex.h>
- +#include <linux/netdevice.h>
- +#include <linux/of.h>
- +#include <linux/of_net.h>
- +#include <linux/phylink.h>
- +#include <linux/platform_device.h>
- +#include <linux/sfp.h>
- +#include <linux/slab.h>
- +#include <linux/workqueue.h>
- +
- +#include "mdio-i2c.h"
- +#include "swphy.h"
- +
- +enum {
- + GPIO_MODDEF0,
- + GPIO_LOS,
- + GPIO_TX_FAULT,
- + GPIO_TX_DISABLE,
- + GPIO_RATE_SELECT,
- + GPIO_MAX,
- +
- + SFP_F_PRESENT = BIT(GPIO_MODDEF0),
- + SFP_F_LOS = BIT(GPIO_LOS),
- + SFP_F_TX_FAULT = BIT(GPIO_TX_FAULT),
- + SFP_F_TX_DISABLE = BIT(GPIO_TX_DISABLE),
- + SFP_F_RATE_SELECT = BIT(GPIO_RATE_SELECT),
- +
- + SFP_E_INSERT = 0,
- + SFP_E_REMOVE,
- + SFP_E_DEV_DOWN,
- + SFP_E_DEV_UP,
- + SFP_E_TX_FAULT,
- + SFP_E_TX_CLEAR,
- + SFP_E_LOS_HIGH,
- + SFP_E_LOS_LOW,
- + SFP_E_TIMEOUT,
- +
- + SFP_MOD_EMPTY = 0,
- + SFP_MOD_PROBE,
- + SFP_MOD_PRESENT,
- + SFP_MOD_ERROR,
- +
- + SFP_DEV_DOWN = 0,
- + SFP_DEV_UP,
- +
- + SFP_S_DOWN = 0,
- + SFP_S_INIT,
- + SFP_S_WAIT_LOS,
- + SFP_S_LINK_UP,
- + SFP_S_TX_FAULT,
- + SFP_S_REINIT,
- + SFP_S_TX_DISABLE,
- +};
- +
- +static const char *gpio_of_names[] = {
- + "moddef0",
- + "los",
- + "tx-fault",
- + "tx-disable",
- + "rate-select",
- +};
- +
- +static const enum gpiod_flags gpio_flags[] = {
- + GPIOD_IN,
- + GPIOD_IN,
- + GPIOD_IN,
- + GPIOD_ASIS,
- + GPIOD_ASIS,
- +};
- +
- +#define T_INIT_JIFFIES msecs_to_jiffies(300)
- +#define T_RESET_US 10
- +#define T_FAULT_RECOVER msecs_to_jiffies(1000)
- +
- +/* SFP module presence detection is poor: the three MOD DEF signals are
- + * the same length on the PCB, which means it's possible for MOD DEF 0 to
- + * connect before the I2C bus on MOD DEF 1/2. Try to work around this
- + * design bug by waiting 50ms before probing, and then retry every 250ms.
- + */
- +#define T_PROBE_INIT msecs_to_jiffies(50)
- +#define T_PROBE_RETRY msecs_to_jiffies(250)
- +
- +/*
- + * SFP modules appear to always have their PHY configured for bus address
- + * 0x56 (which with mdio-i2c, translates to a PHY address of 22).
- + */
- +#define SFP_PHY_ADDR 22
- +
- +/*
- + * Give this long for the PHY to reset.
- + */
- +#define T_PHY_RESET_MS 50
- +
- +static DEFINE_MUTEX(sfp_mutex);
- +
- +struct sfp {
- + struct device *dev;
- + struct i2c_adapter *i2c;
- + struct mii_bus *i2c_mii;
- + struct net_device *ndev;
- + struct phylink *phylink;
- + struct phy_device *mod_phy;
- +
- + unsigned int (*get_state)(struct sfp *);
- + void (*set_state)(struct sfp *, unsigned int);
- + int (*read)(struct sfp *, bool, u8, void *, size_t);
- +
- + struct gpio_desc *gpio[GPIO_MAX];
- +
- + unsigned int state;
- + struct delayed_work poll;
- + struct delayed_work timeout;
- + struct mutex sm_mutex;
- + unsigned char sm_mod_state;
- + unsigned char sm_dev_state;
- + unsigned short sm_state;
- + unsigned int sm_retries;
- +
- + struct sfp_eeprom_id id;
- +
- + struct notifier_block netdev_nb;
- +};
- +
- +static unsigned long poll_jiffies;
- +
- +static unsigned int sfp_gpio_get_state(struct sfp *sfp)
- +{
- + unsigned int i, state, v;
- +
- + for (i = state = 0; i < GPIO_MAX; i++) {
- + if (gpio_flags[i] != GPIOD_IN || !sfp->gpio[i])
- + continue;
- +
- + v = gpiod_get_value_cansleep(sfp->gpio[i]);
- + if (v)
- + state |= BIT(i);
- + }
- +
- + return state;
- +}
- +
- +static void sfp_gpio_set_state(struct sfp *sfp, unsigned int state)
- +{
- + if (state & SFP_F_PRESENT) {
- + /* If the module is present, drive the signals */
- + if (sfp->gpio[GPIO_TX_DISABLE])
- + gpiod_direction_output(sfp->gpio[GPIO_TX_DISABLE],
- + state & SFP_F_TX_DISABLE);
- + if (state & SFP_F_RATE_SELECT)
- + gpiod_direction_output(sfp->gpio[GPIO_RATE_SELECT],
- + state & SFP_F_RATE_SELECT);
- + } else {
- + /* Otherwise, let them float to the pull-ups */
- + if (sfp->gpio[GPIO_TX_DISABLE])
- + gpiod_direction_input(sfp->gpio[GPIO_TX_DISABLE]);
- + if (state & SFP_F_RATE_SELECT)
- + gpiod_direction_input(sfp->gpio[GPIO_RATE_SELECT]);
- + }
- +}
- +
- +static int sfp__i2c_read(struct i2c_adapter *i2c, u8 bus_addr, u8 dev_addr,
- + void *buf, size_t len)
- +{
- + struct i2c_msg msgs[2];
- + int ret;
- +
- + msgs[0].addr = bus_addr;
- + msgs[0].flags = 0;
- + msgs[0].len = 1;
- + msgs[0].buf = &dev_addr;
- + msgs[1].addr = bus_addr;
- + msgs[1].flags = I2C_M_RD;
- + msgs[1].len = len;
- + msgs[1].buf = buf;
- +
- + ret = i2c_transfer(i2c, msgs, ARRAY_SIZE(msgs));
- + if (ret < 0)
- + return ret;
- +
- + return ret == ARRAY_SIZE(msgs) ? len : 0;
- +}
- +
- +static int sfp_i2c_read(struct sfp *sfp, bool a2, u8 addr, void *buf,
- + size_t len)
- +{
- + return sfp__i2c_read(sfp->i2c, a2 ? 0x51 : 0x50, addr, buf, len);
- +}
- +
- +static int sfp_i2c_configure(struct sfp *sfp, struct i2c_adapter *i2c)
- +{
- + struct mii_bus *i2c_mii;
- + int ret;
- +
- + if (!i2c_check_functionality(i2c, I2C_FUNC_I2C))
- + return -EINVAL;
- +
- + sfp->i2c = i2c;
- + sfp->read = sfp_i2c_read;
- +
- + i2c_mii = mdio_i2c_alloc(sfp->dev, i2c);
- + if (IS_ERR(i2c_mii))
- + return PTR_ERR(i2c_mii);
- +
- + i2c_mii->name = "SFP I2C Bus";
- + i2c_mii->phy_mask = ~0;
- +
- + ret = mdiobus_register(i2c_mii);
- + if (ret < 0) {
- + mdiobus_free(i2c_mii);
- + return ret;
- + }
- +
- + sfp->i2c_mii = i2c_mii;
- +
- + return 0;
- +}
- +
- +
- +/* Interface */
- +static unsigned int sfp_get_state(struct sfp *sfp)
- +{
- + return sfp->get_state(sfp);
- +}
- +
- +static void sfp_set_state(struct sfp *sfp, unsigned int state)
- +{
- + sfp->set_state(sfp, state);
- +}
- +
- +static int sfp_read(struct sfp *sfp, bool a2, u8 addr, void *buf, size_t len)
- +{
- + return sfp->read(sfp, a2, addr, buf, len);
- +}
- +
- +static unsigned int sfp_check(void *buf, size_t len)
- +{
- + u8 *p, check;
- +
- + for (p = buf, check = 0; len; p++, len--)
- + check += *p;
- +
- + return check;
- +}
- +
- +/* Helpers */
- +static void sfp_module_tx_disable(struct sfp *sfp)
- +{
- + dev_dbg(sfp->dev, "tx disable %u -> %u\n",
- + sfp->state & SFP_F_TX_DISABLE ? 1 : 0, 1);
- + sfp->state |= SFP_F_TX_DISABLE;
- + sfp_set_state(sfp, sfp->state);
- +}
- +
- +static void sfp_module_tx_enable(struct sfp *sfp)
- +{
- + dev_dbg(sfp->dev, "tx disable %u -> %u\n",
- + sfp->state & SFP_F_TX_DISABLE ? 1 : 0, 0);
- + sfp->state &= ~SFP_F_TX_DISABLE;
- + sfp_set_state(sfp, sfp->state);
- +}
- +
- +static void sfp_module_tx_fault_reset(struct sfp *sfp)
- +{
- + unsigned int state = sfp->state;
- +
- + if (state & SFP_F_TX_DISABLE)
- + return;
- +
- + sfp_set_state(sfp, state | SFP_F_TX_DISABLE);
- +
- + udelay(T_RESET_US);
- +
- + sfp_set_state(sfp, state);
- +}
- +
- +/* SFP state machine */
- +static void sfp_sm_set_timer(struct sfp *sfp, unsigned int timeout)
- +{
- + if (timeout)
- + mod_delayed_work(system_power_efficient_wq, &sfp->timeout,
- + timeout);
- + else
- + cancel_delayed_work(&sfp->timeout);
- +}
- +
- +static void sfp_sm_next(struct sfp *sfp, unsigned int state,
- + unsigned int timeout)
- +{
- + sfp->sm_state = state;
- + sfp_sm_set_timer(sfp, timeout);
- +}
- +
- +static void sfp_sm_ins_next(struct sfp *sfp, unsigned int state, unsigned int timeout)
- +{
- + sfp->sm_mod_state = state;
- + sfp_sm_set_timer(sfp, timeout);
- +}
- +
- +static void sfp_sm_phy_detach(struct sfp *sfp)
- +{
- + phy_stop(sfp->mod_phy);
- + if (sfp->phylink)
- + phylink_disconnect_phy(sfp->phylink);
- + phy_device_remove(sfp->mod_phy);
- + phy_device_free(sfp->mod_phy);
- + sfp->mod_phy = NULL;
- +}
- +
- +static void sfp_sm_probe_phy(struct sfp *sfp)
- +{
- + struct phy_device *phy;
- + int err;
- +
- + msleep(T_PHY_RESET_MS);
- +
- + phy = mdiobus_scan(sfp->i2c_mii, SFP_PHY_ADDR);
- + if (IS_ERR(phy)) {
- + dev_err(sfp->dev, "mdiobus scan returned %ld\n", PTR_ERR(phy));
- + return;
- + }
- + if (!phy) {
- + dev_info(sfp->dev, "no PHY detected\n");
- + return;
- + }
- +
- + err = phylink_connect_phy(sfp->phylink, phy);
- + if (err) {
- + phy_device_remove(phy);
- + phy_device_free(phy);
- + dev_err(sfp->dev, "phylink_connect_phy failed: %d\n", err);
- + return;
- + }
- +
- + sfp->mod_phy = phy;
- + phy_start(phy);
- +}
- +
- +static void sfp_sm_link_up(struct sfp *sfp)
- +{
- + if (sfp->phylink)
- + phylink_enable(sfp->phylink);
- +
- + sfp_sm_next(sfp, SFP_S_LINK_UP, 0);
- +}
- +
- +static void sfp_sm_link_down(struct sfp *sfp)
- +{
- + if (sfp->phylink)
- + phylink_disable(sfp->phylink);
- +}
- +
- +static void sfp_sm_link_check_los(struct sfp *sfp)
- +{
- + unsigned int los = sfp->state & SFP_F_LOS;
- +
- + /* FIXME: what if neither SFP_OPTIONS_LOS_INVERTED nor
- + * SFP_OPTIONS_LOS_NORMAL are set? For now, we assume
- + * the same as SFP_OPTIONS_LOS_NORMAL set.
- + */
- + if (sfp->id.ext.options & SFP_OPTIONS_LOS_INVERTED)
- + los ^= SFP_F_LOS;
- +
- + if (los)
- + sfp_sm_next(sfp, SFP_S_WAIT_LOS, 0);
- + else
- + sfp_sm_link_up(sfp);
- +}
- +
- +static void sfp_sm_fault(struct sfp *sfp, bool warn)
- +{
- + if (sfp->sm_retries && !--sfp->sm_retries) {
- + dev_err(sfp->dev, "module persistently indicates fault, disabling\n");
- + sfp_sm_next(sfp, SFP_S_TX_DISABLE, 0);
- + } else {
- + if (warn)
- + dev_err(sfp->dev, "module transmit fault indicated\n");
- +
- + sfp_sm_next(sfp, SFP_S_TX_FAULT, T_FAULT_RECOVER);
- + }
- +}
- +
- +static void sfp_sm_mod_init(struct sfp *sfp)
- +{
- + sfp_module_tx_enable(sfp);
- +
- + /* Wait t_init before indicating that the link is up, provided the
- + * current state indicates no TX_FAULT. If TX_FAULT clears before
- + * this time, that's fine too.
- + */
- + sfp_sm_next(sfp, SFP_S_INIT, T_INIT_JIFFIES);
- + sfp->sm_retries = 5;
- +
- + if (sfp->phylink) {
- + /* Setting the serdes link mode is guesswork: there's no
- + * field in the EEPROM which indicates what mode should
- + * be used.
- + *
- + * If it's a gigabit-only fiber module, it probably does
- + * not have a PHY, so switch to 802.3z negotiation mode.
- + * Otherwise, switch to SGMII mode (which is required to
- + * support non-gigabit speeds) and probe for a PHY.
- + */
- + if (!sfp->id.base.e1000_base_t &&
- + !sfp->id.base.e100_base_lx &&
- + !sfp->id.base.e100_base_fx) {
- + phylink_set_link_an_mode(sfp->phylink, MLO_AN_8023Z);
- + } else {
- + phylink_set_link_an_mode(sfp->phylink, MLO_AN_SGMII);
- + sfp_sm_probe_phy(sfp);
- + }
- + }
- +}
- +
- +static int sfp_sm_mod_probe(struct sfp *sfp)
- +{
- + /* SFP module inserted - read I2C data */
- + struct sfp_eeprom_id id;
- + char vendor[17];
- + char part[17];
- + char sn[17];
- + char date[9];
- + char rev[5];
- + u8 check;
- + int err;
- +
- + err = sfp_read(sfp, false, 0, &id, sizeof(id));
- + if (err < 0) {
- + dev_err(sfp->dev, "failed to read EEPROM: %d\n", err);
- + return -EAGAIN;
- + }
- +
- + /* Validate the checksum over the base structure */
- + check = sfp_check(&id.base, sizeof(id.base) - 1);
- + if (check != id.base.cc_base) {
- + dev_err(sfp->dev,
- + "EEPROM base structure checksum failure: 0x%02x\n",
- + check);
- + return -EINVAL;
- + }
- +
- + check = sfp_check(&id.ext, sizeof(id.ext) - 1);
- + if (check != id.ext.cc_ext) {
- + dev_err(sfp->dev,
- + "EEPROM extended structure checksum failure: 0x%02x\n",
- + check);
- + memset(&id.ext, 0, sizeof(id.ext));
- + }
- +
- + sfp->id = id;
- +
- + memcpy(vendor, sfp->id.base.vendor_name, 16);
- + vendor[16] = '\0';
- + memcpy(part, sfp->id.base.vendor_pn, 16);
- + part[16] = '\0';
- + memcpy(rev, sfp->id.base.vendor_rev, 4);
- + rev[4] = '\0';
- + memcpy(sn, sfp->id.ext.vendor_sn, 16);
- + sn[16] = '\0';
- + memcpy(date, sfp->id.ext.datecode, 8);
- + date[8] = '\0';
- +
- + dev_info(sfp->dev, "module %s %s rev %s sn %s dc %s\n", vendor, part, rev, sn, date);
- +
- + /* We only support SFP modules, not the legacy GBIC modules. */
- + if (sfp->id.base.phys_id != SFP_PHYS_ID_SFP ||
- + sfp->id.base.phys_ext_id != SFP_PHYS_EXT_ID_SFP) {
- + dev_err(sfp->dev, "module is not SFP - phys id 0x%02x 0x%02x\n",
- + sfp->id.base.phys_id, sfp->id.base.phys_ext_id);
- + return -EINVAL;
- + }
- +
- + /*
- + * What isn't clear from the SFP documentation is whether this
- + * specifies the encoding expected on the TD/RD lines, or whether
- + * the TD/RD lines are always 8b10b encoded, but the transceiver
- + * converts. Eg, think of a copper SFP supporting 1G/100M/10M
- + * ethernet: this requires 8b10b encoding for 1G, 4b5b for 100M,
- + * and manchester for 10M.
- + */
- + /* 1Gbit ethernet requires 8b10b encoding */
- + if (sfp->id.base.encoding != SFP_ENCODING_8B10B) {
- + dev_err(sfp->dev, "module does not support 8B10B encoding\n");
- + return -EINVAL;
- + }
- +
- + if (sfp->phylink) {
- + u32 support;
- + u8 port;
- +
- + if (sfp->id.base.e1000_base_t) {
- + support = SUPPORTED_TP;
- + port = PORT_TP;
- + } else {
- + support = SUPPORTED_FIBRE;
- + port = PORT_FIBRE;
- + }
- + phylink_set_link_port(sfp->phylink, support, port);
- + }
- +
- + return 0;
- +}
- +
- +static void sfp_sm_mod_remove(struct sfp *sfp)
- +{
- + if (sfp->mod_phy)
- + sfp_sm_phy_detach(sfp);
- +
- + sfp_module_tx_disable(sfp);
- +
- + memset(&sfp->id, 0, sizeof(sfp->id));
- +
- + dev_info(sfp->dev, "module removed\n");
- +}
- +
- +static void sfp_sm_event(struct sfp *sfp, unsigned int event)
- +{
- + mutex_lock(&sfp->sm_mutex);
- +
- + dev_dbg(sfp->dev, "SM: enter %u:%u:%u event %u\n",
- + sfp->sm_mod_state, sfp->sm_dev_state, sfp->sm_state, event);
- +
- + /* This state machine tracks the insert/remove state of
- + * the module, and handles probing the on-board EEPROM.
- + */
- + switch (sfp->sm_mod_state) {
- + default:
- + if (event == SFP_E_INSERT) {
- + sfp_module_tx_disable(sfp);
- + sfp_sm_ins_next(sfp, SFP_MOD_PROBE, T_PROBE_INIT);
- + }
- + break;
- +
- + case SFP_MOD_PROBE:
- + if (event == SFP_E_REMOVE) {
- + sfp_sm_ins_next(sfp, SFP_MOD_EMPTY, 0);
- + } else if (event == SFP_E_TIMEOUT) {
- + int err = sfp_sm_mod_probe(sfp);
- +
- + if (err == 0)
- + sfp_sm_ins_next(sfp, SFP_MOD_PRESENT, 0);
- + else if (err == -EAGAIN)
- + sfp_sm_set_timer(sfp, T_PROBE_RETRY);
- + else
- + sfp_sm_ins_next(sfp, SFP_MOD_ERROR, 0);
- + }
- + break;
- +
- + case SFP_MOD_PRESENT:
- + case SFP_MOD_ERROR:
- + if (event == SFP_E_REMOVE) {
- + sfp_sm_mod_remove(sfp);
- + sfp_sm_ins_next(sfp, SFP_MOD_EMPTY, 0);
- + }
- + break;
- + }
- +
- + /* This state machine tracks the netdev up/down state */
- + switch (sfp->sm_dev_state) {
- + default:
- + if (event == SFP_E_DEV_UP)
- + sfp->sm_dev_state = SFP_DEV_UP;
- + break;
- +
- + case SFP_DEV_UP:
- + if (event == SFP_E_DEV_DOWN) {
- + /* If the module has a PHY, avoid raising TX disable
- + * as this resets the PHY. Otherwise, raise it to
- + * turn the laser off.
- + */
- + if (!sfp->mod_phy)
- + sfp_module_tx_disable(sfp);
- + sfp->sm_dev_state = SFP_DEV_DOWN;
- + }
- + break;
- + }
- +
- + /* Some events are global */
- + if (sfp->sm_state != SFP_S_DOWN &&
- + (sfp->sm_mod_state != SFP_MOD_PRESENT ||
- + sfp->sm_dev_state != SFP_DEV_UP)) {
- + if (sfp->sm_state == SFP_S_LINK_UP &&
- + sfp->sm_dev_state == SFP_DEV_UP)
- + sfp_sm_link_down(sfp);
- + if (sfp->mod_phy)
- + sfp_sm_phy_detach(sfp);
- + sfp_sm_next(sfp, SFP_S_DOWN, 0);
- + mutex_unlock(&sfp->sm_mutex);
- + return;
- + }
- +
- + /* The main state machine */
- + switch (sfp->sm_state) {
- + case SFP_S_DOWN:
- + if (sfp->sm_mod_state == SFP_MOD_PRESENT &&
- + sfp->sm_dev_state == SFP_DEV_UP)
- + sfp_sm_mod_init(sfp);
- + break;
- +
- + case SFP_S_INIT:
- + if (event == SFP_E_TIMEOUT && sfp->state & SFP_F_TX_FAULT)
- + sfp_sm_fault(sfp, true);
- + else if (event == SFP_E_TIMEOUT || event == SFP_E_TX_CLEAR)
- + sfp_sm_link_check_los(sfp);
- + break;
- +
- + case SFP_S_WAIT_LOS:
- + if (event == SFP_E_TX_FAULT)
- + sfp_sm_fault(sfp, true);
- + else if (event ==
- + (sfp->id.ext.options & SFP_OPTIONS_LOS_INVERTED ?
- + SFP_E_LOS_HIGH : SFP_E_LOS_LOW))
- + sfp_sm_link_up(sfp);
- + break;
- +
- + case SFP_S_LINK_UP:
- + if (event == SFP_E_TX_FAULT) {
- + sfp_sm_link_down(sfp);
- + sfp_sm_fault(sfp, true);
- + } else if (event ==
- + (sfp->id.ext.options & SFP_OPTIONS_LOS_INVERTED ?
- + SFP_E_LOS_LOW : SFP_E_LOS_HIGH)) {
- + sfp_sm_link_down(sfp);
- + sfp_sm_next(sfp, SFP_S_WAIT_LOS, 0);
- + }
- + break;
- +
- + case SFP_S_TX_FAULT:
- + if (event == SFP_E_TIMEOUT) {
- + sfp_module_tx_fault_reset(sfp);
- + sfp_sm_next(sfp, SFP_S_REINIT, T_INIT_JIFFIES);
- + }
- + break;
- +
- + case SFP_S_REINIT:
- + if (event == SFP_E_TIMEOUT && sfp->state & SFP_F_TX_FAULT) {
- + sfp_sm_fault(sfp, false);
- + } else if (event == SFP_E_TIMEOUT || event == SFP_E_TX_CLEAR) {
- + dev_info(sfp->dev, "module transmit fault recovered\n");
- + sfp_sm_link_check_los(sfp);
- + }
- + break;
- +
- + case SFP_S_TX_DISABLE:
- + break;
- + }
- +
- + dev_dbg(sfp->dev, "SM: exit %u:%u:%u\n",
- + sfp->sm_mod_state, sfp->sm_dev_state, sfp->sm_state);
- +
- + mutex_unlock(&sfp->sm_mutex);
- +}
- +
- +#if 0
- +static int sfp_phy_module_info(struct phy_device *phy,
- + struct ethtool_modinfo *modinfo)
- +{
- + struct sfp *sfp = phy->priv;
- +
- + /* locking... and check module is present */
- +
- + if (sfp->id.ext.sff8472_compliance) {
- + modinfo->type = ETH_MODULE_SFF_8472;
- + modinfo->eeprom_len = ETH_MODULE_SFF_8472_LEN;
- + } else {
- + modinfo->type = ETH_MODULE_SFF_8079;
- + modinfo->eeprom_len = ETH_MODULE_SFF_8079_LEN;
- + }
- + return 0;
- +}
- +
- +static int sfp_phy_module_eeprom(struct phy_device *phy,
- + struct ethtool_eeprom *ee, u8 *data)
- +{
- + struct sfp *sfp = phy->priv;
- + unsigned int first, last, len;
- + int ret;
- +
- + if (ee->len == 0)
- + return -EINVAL;
- +
- + first = ee->offset;
- + last = ee->offset + ee->len;
- + if (first < ETH_MODULE_SFF_8079_LEN) {
- + len = last;
- + if (len > ETH_MODULE_SFF_8079_LEN)
- + len = ETH_MODULE_SFF_8079_LEN;
- + len -= first;
- +
- + ret = sfp->read(sfp, false, first, data, len);
- + if (ret < 0)
- + return ret;
- +
- + first += len;
- + data += len;
- + }
- + if (first >= ETH_MODULE_SFF_8079_LEN && last > first) {
- + len = last - first;
- +
- + ret = sfp->read(sfp, true, first, data, len);
- + if (ret < 0)
- + return ret;
- + }
- + return 0;
- +}
- +#endif
- +
- +static void sfp_timeout(struct work_struct *work)
- +{
- + struct sfp *sfp = container_of(work, struct sfp, timeout.work);
- +
- + sfp_sm_event(sfp, SFP_E_TIMEOUT);
- +}
- +
- +static void sfp_check_state(struct sfp *sfp)
- +{
- + unsigned int state, i, changed;
- +
- + state = sfp_get_state(sfp);
- + changed = state ^ sfp->state;
- + changed &= SFP_F_PRESENT | SFP_F_LOS | SFP_F_TX_FAULT;
- +
- + for (i = 0; i < GPIO_MAX; i++)
- + if (changed & BIT(i))
- + dev_dbg(sfp->dev, "%s %u -> %u\n", gpio_of_names[i],
- + !!(sfp->state & BIT(i)), !!(state & BIT(i)));
- +
- + state |= sfp->state & (SFP_F_TX_DISABLE | SFP_F_RATE_SELECT);
- + sfp->state = state;
- +
- + if (changed & SFP_F_PRESENT)
- + sfp_sm_event(sfp, state & SFP_F_PRESENT ?
- + SFP_E_INSERT : SFP_E_REMOVE);
- +
- + if (changed & SFP_F_TX_FAULT)
- + sfp_sm_event(sfp, state & SFP_F_TX_FAULT ?
- + SFP_E_TX_FAULT : SFP_E_TX_CLEAR);
- +
- + if (changed & SFP_F_LOS)
- + sfp_sm_event(sfp, state & SFP_F_LOS ?
- + SFP_E_LOS_HIGH : SFP_E_LOS_LOW);
- +}
- +
- +static irqreturn_t sfp_irq(int irq, void *data)
- +{
- + struct sfp *sfp = data;
- +
- + sfp_check_state(sfp);
- +
- + return IRQ_HANDLED;
- +}
- +
- +static void sfp_poll(struct work_struct *work)
- +{
- + struct sfp *sfp = container_of(work, struct sfp, poll.work);
- +
- + sfp_check_state(sfp);
- + mod_delayed_work(system_wq, &sfp->poll, poll_jiffies);
- +}
- +
- +static int sfp_netdev_notify(struct notifier_block *nb, unsigned long act, void *data)
- +{
- + struct sfp *sfp = container_of(nb, struct sfp, netdev_nb);
- + struct netdev_notifier_info *info = data;
- + struct net_device *ndev = info->dev;
- +
- + if (!sfp->ndev || ndev != sfp->ndev)
- + return NOTIFY_DONE;
- +
- + switch (act) {
- + case NETDEV_UP:
- + sfp_sm_event(sfp, SFP_E_DEV_UP);
- + break;
- +
- + case NETDEV_GOING_DOWN:
- + sfp_sm_event(sfp, SFP_E_DEV_DOWN);
- + break;
- +
- + case NETDEV_UNREGISTER:
- + if (sfp->mod_phy && sfp->phylink)
- + phylink_disconnect_phy(sfp->phylink);
- + sfp->phylink = NULL;
- + dev_put(sfp->ndev);
- + sfp->ndev = NULL;
- + break;
- + }
- + return NOTIFY_OK;
- +}
- +
- +static struct sfp *sfp_alloc(struct device *dev)
- +{
- + struct sfp *sfp;
- +
- + sfp = kzalloc(sizeof(*sfp), GFP_KERNEL);
- + if (!sfp)
- + return ERR_PTR(-ENOMEM);
- +
- + sfp->dev = dev;
- +
- + mutex_init(&sfp->sm_mutex);
- + INIT_DELAYED_WORK(&sfp->poll, sfp_poll);
- + INIT_DELAYED_WORK(&sfp->timeout, sfp_timeout);
- +
- + sfp->netdev_nb.notifier_call = sfp_netdev_notify;
- +
- + return sfp;
- +}
- +
- +static void sfp_destroy(struct sfp *sfp)
- +{
- + cancel_delayed_work_sync(&sfp->poll);
- + cancel_delayed_work_sync(&sfp->timeout);
- + if (sfp->i2c_mii) {
- + mdiobus_unregister(sfp->i2c_mii);
- + mdiobus_free(sfp->i2c_mii);
- + }
- + if (sfp->i2c)
- + i2c_put_adapter(sfp->i2c);
- + of_node_put(sfp->dev->of_node);
- + kfree(sfp);
- +}
- +
- +static void sfp_cleanup(void *data)
- +{
- + struct sfp *sfp = data;
- +
- + sfp_destroy(sfp);
- +}
- +
- +static int sfp_probe(struct platform_device *pdev)
- +{
- + struct sfp *sfp;
- + bool poll = false;
- + int irq, err, i;
- +
- + sfp = sfp_alloc(&pdev->dev);
- + if (IS_ERR(sfp))
- + return PTR_ERR(sfp);
- +
- + platform_set_drvdata(pdev, sfp);
- +
- + err = devm_add_action(sfp->dev, sfp_cleanup, sfp);
- + if (err < 0)
- + return err;
- +
- + if (pdev->dev.of_node) {
- + struct device_node *node = pdev->dev.of_node;
- + struct device_node *np;
- +
- + np = of_parse_phandle(node, "i2c-bus", 0);
- + if (np) {
- + struct i2c_adapter *i2c;
- +
- + i2c = of_find_i2c_adapter_by_node(np);
- + of_node_put(np);
- + if (!i2c)
- + return -EPROBE_DEFER;
- +
- + err = sfp_i2c_configure(sfp, i2c);
- + if (err < 0) {
- + i2c_put_adapter(i2c);
- + return err;
- + }
- + }
- +
- + for (i = 0; i < GPIO_MAX; i++) {
- + sfp->gpio[i] = devm_gpiod_get_optional(sfp->dev,
- + gpio_of_names[i], gpio_flags[i]);
- + if (IS_ERR(sfp->gpio[i]))
- + return PTR_ERR(sfp->gpio[i]);
- + }
- +
- + sfp->get_state = sfp_gpio_get_state;
- + sfp->set_state = sfp_gpio_set_state;
- +
- + np = of_parse_phandle(node, "sfp,ethernet", 0);
- + if (!np) {
- + dev_err(sfp->dev, "missing sfp,ethernet property\n");
- + return -EINVAL;
- + }
- +
- + sfp->ndev = of_find_net_device_by_node(np);
- + if (!sfp->ndev) {
- + dev_err(sfp->dev, "ethernet device not found\n");
- + return -EPROBE_DEFER;
- + }
- +
- + dev_hold(sfp->ndev);
- + put_device(&sfp->ndev->dev);
- +
- + sfp->phylink = phylink_lookup_by_netdev(sfp->ndev);
- + if (!sfp->phylink) {
- + dev_err(sfp->dev, "ethernet device not found\n");
- + return -EPROBE_DEFER;
- + }
- +
- + phylink_disable(sfp->phylink);
- + }
- +
- + sfp->state = sfp_get_state(sfp);
- + if (sfp->gpio[GPIO_TX_DISABLE] &&
- + gpiod_get_value_cansleep(sfp->gpio[GPIO_TX_DISABLE]))
- + sfp->state |= SFP_F_TX_DISABLE;
- + if (sfp->gpio[GPIO_RATE_SELECT] &&
- + gpiod_get_value_cansleep(sfp->gpio[GPIO_RATE_SELECT]))
- + sfp->state |= SFP_F_RATE_SELECT;
- + sfp_set_state(sfp, sfp->state);
- + sfp_module_tx_disable(sfp);
- + if (sfp->state & SFP_F_PRESENT)
- + sfp_sm_event(sfp, SFP_E_INSERT);
- +
- + for (i = 0; i < GPIO_MAX; i++) {
- + if (gpio_flags[i] != GPIOD_IN || !sfp->gpio[i])
- + continue;
- +
- + irq = gpiod_to_irq(sfp->gpio[i]);
- + if (!irq) {
- + poll = true;
- + continue;
- + }
- +
- + err = devm_request_threaded_irq(sfp->dev, irq, NULL, sfp_irq,
- + IRQF_ONESHOT |
- + IRQF_TRIGGER_RISING |
- + IRQF_TRIGGER_FALLING,
- + dev_name(sfp->dev), sfp);
- + if (err)
- + poll = true;
- + }
- +
- + if (poll)
- + mod_delayed_work(system_wq, &sfp->poll, poll_jiffies);
- +
- + register_netdevice_notifier(&sfp->netdev_nb);
- +
- + return 0;
- +}
- +
- +static int sfp_remove(struct platform_device *pdev)
- +{
- + struct sfp *sfp = platform_get_drvdata(pdev);
- +
- + unregister_netdevice_notifier(&sfp->netdev_nb);
- + if (sfp->ndev)
- + dev_put(sfp->ndev);
- +
- + return 0;
- +}
- +
- +static const struct of_device_id sfp_of_match[] = {
- + { .compatible = "sff,sfp", },
- + { },
- +};
- +MODULE_DEVICE_TABLE(of, sfp_of_match);
- +
- +static struct platform_driver sfp_driver = {
- + .probe = sfp_probe,
- + .remove = sfp_remove,
- + .driver = {
- + .name = "sfp",
- + .of_match_table = sfp_of_match,
- + },
- +};
- +
- +static int sfp_init(void)
- +{
- + poll_jiffies = msecs_to_jiffies(100);
- +
- + return platform_driver_register(&sfp_driver);
- +}
- +module_init(sfp_init);
- +
- +static void sfp_exit(void)
- +{
- + platform_driver_unregister(&sfp_driver);
- +}
- +module_exit(sfp_exit);
- +
- +MODULE_ALIAS("platform:sfp");
- +MODULE_AUTHOR("Russell King");
- +MODULE_LICENSE("GPL v2");
- --- /dev/null
- +++ b/include/linux/sfp.h
- @@ -0,0 +1,339 @@
- +#ifndef LINUX_SFP_H
- +#define LINUX_SFP_H
- +
- +struct __packed sfp_eeprom_base {
- + u8 phys_id;
- + u8 phys_ext_id;
- + u8 connector;
- +#if defined __BIG_ENDIAN_BITFIELD
- + u8 e10g_base_er:1;
- + u8 e10g_base_lrm:1;
- + u8 e10g_base_lr:1;
- + u8 e10g_base_sr:1;
- + u8 if_1x_sx:1;
- + u8 if_1x_lx:1;
- + u8 if_1x_copper_active:1;
- + u8 if_1x_copper_passive:1;
- +
- + u8 escon_mmf_1310_led:1;
- + u8 escon_smf_1310_laser:1;
- + u8 sonet_oc192_short_reach:1;
- + u8 sonet_reach_bit1:1;
- + u8 sonet_reach_bit2:1;
- + u8 sonet_oc48_long_reach:1;
- + u8 sonet_oc48_intermediate_reach:1;
- + u8 sonet_oc48_short_reach:1;
- +
- + u8 unallocated_5_7:1;
- + u8 sonet_oc12_smf_long_reach:1;
- + u8 sonet_oc12_smf_intermediate_reach:1;
- + u8 sonet_oc12_short_reach:1;
- + u8 unallocated_5_3:1;
- + u8 sonet_oc3_smf_long_reach:1;
- + u8 sonet_oc3_smf_intermediate_reach:1;
- + u8 sonet_oc3_short_reach:1;
- +
- + u8 e_base_px:1;
- + u8 e_base_bx10:1;
- + u8 e100_base_fx:1;
- + u8 e100_base_lx:1;
- + u8 e1000_base_t:1;
- + u8 e1000_base_cx:1;
- + u8 e1000_base_lx:1;
- + u8 e1000_base_sx:1;
- +
- + u8 fc_ll_v:1;
- + u8 fc_ll_s:1;
- + u8 fc_ll_i:1;
- + u8 fc_ll_l:1;
- + u8 fc_ll_m:1;
- + u8 fc_tech_sa:1;
- + u8 fc_tech_lc:1;
- + u8 fc_tech_electrical_inter_enclosure:1;
- +
- + u8 fc_tech_electrical_intra_enclosure:1;
- + u8 fc_tech_sn:1;
- + u8 fc_tech_sl:1;
- + u8 fc_tech_ll:1;
- + u8 sfp_ct_active:1;
- + u8 sfp_ct_passive:1;
- + u8 unallocated_8_1:1;
- + u8 unallocated_8_0:1;
- +
- + u8 fc_media_tw:1;
- + u8 fc_media_tp:1;
- + u8 fc_media_mi:1;
- + u8 fc_media_tv:1;
- + u8 fc_media_m6:1;
- + u8 fc_media_m5:1;
- + u8 unallocated_9_1:1;
- + u8 fc_media_sm:1;
- +
- + u8 fc_speed_1200:1;
- + u8 fc_speed_800:1;
- + u8 fc_speed_1600:1;
- + u8 fc_speed_400:1;
- + u8 fc_speed_3200:1;
- + u8 fc_speed_200:1;
- + u8 unallocated_10_1:1;
- + u8 fc_speed_100:1;
- +#elif defined __LITTLE_ENDIAN_BITFIELD
- + u8 if_1x_copper_passive:1;
- + u8 if_1x_copper_active:1;
- + u8 if_1x_lx:1;
- + u8 if_1x_sx:1;
- + u8 e10g_base_sr:1;
- + u8 e10g_base_lr:1;
- + u8 e10g_base_lrm:1;
- + u8 e10g_base_er:1;
- +
- + u8 sonet_oc3_short_reach:1;
- + u8 sonet_oc3_smf_intermediate_reach:1;
- + u8 sonet_oc3_smf_long_reach:1;
- + u8 unallocated_5_3:1;
- + u8 sonet_oc12_short_reach:1;
- + u8 sonet_oc12_smf_intermediate_reach:1;
- + u8 sonet_oc12_smf_long_reach:1;
- + u8 unallocated_5_7:1;
- +
- + u8 sonet_oc48_short_reach:1;
- + u8 sonet_oc48_intermediate_reach:1;
- + u8 sonet_oc48_long_reach:1;
- + u8 sonet_reach_bit2:1;
- + u8 sonet_reach_bit1:1;
- + u8 sonet_oc192_short_reach:1;
- + u8 escon_smf_1310_laser:1;
- + u8 escon_mmf_1310_led:1;
- +
- + u8 e1000_base_sx:1;
- + u8 e1000_base_lx:1;
- + u8 e1000_base_cx:1;
- + u8 e1000_base_t:1;
- + u8 e100_base_lx:1;
- + u8 e100_base_fx:1;
- + u8 e_base_bx10:1;
- + u8 e_base_px:1;
- +
- + u8 fc_tech_electrical_inter_enclosure:1;
- + u8 fc_tech_lc:1;
- + u8 fc_tech_sa:1;
- + u8 fc_ll_m:1;
- + u8 fc_ll_l:1;
- + u8 fc_ll_i:1;
- + u8 fc_ll_s:1;
- + u8 fc_ll_v:1;
- +
- + u8 unallocated_8_0:1;
- + u8 unallocated_8_1:1;
- + u8 sfp_ct_passive:1;
- + u8 sfp_ct_active:1;
- + u8 fc_tech_ll:1;
- + u8 fc_tech_sl:1;
- + u8 fc_tech_sn:1;
- + u8 fc_tech_electrical_intra_enclosure:1;
- +
- + u8 fc_media_sm:1;
- + u8 unallocated_9_1:1;
- + u8 fc_media_m5:1;
- + u8 fc_media_m6:1;
- + u8 fc_media_tv:1;
- + u8 fc_media_mi:1;
- + u8 fc_media_tp:1;
- + u8 fc_media_tw:1;
- +
- + u8 fc_speed_100:1;
- + u8 unallocated_10_1:1;
- + u8 fc_speed_200:1;
- + u8 fc_speed_3200:1;
- + u8 fc_speed_400:1;
- + u8 fc_speed_1600:1;
- + u8 fc_speed_800:1;
- + u8 fc_speed_1200:1;
- +#else
- +#error Unknown Endian
- +#endif
- + u8 encoding;
- + u8 br_nominal;
- + u8 rate_id;
- + u8 link_len[6];
- + char vendor_name[16];
- + u8 reserved36;
- + char vendor_oui[3];
- + char vendor_pn[16];
- + char vendor_rev[4];
- + union {
- + __be16 optical_wavelength;
- + u8 cable_spec;
- + };
- + u8 reserved62;
- + u8 cc_base;
- +};
- +
- +struct __packed sfp_eeprom_ext {
- + __be16 options;
- + u8 br_max;
- + u8 br_min;
- + char vendor_sn[16];
- + char datecode[8];
- + u8 diagmon;
- + u8 enhopts;
- + u8 sff8472_compliance;
- + u8 cc_ext;
- +};
- +
- +struct __packed sfp_eeprom_id {
- + struct sfp_eeprom_base base;
- + struct sfp_eeprom_ext ext;
- +};
- +
- +/* SFP EEPROM registers */
- +enum {
- + SFP_PHYS_ID = 0x00,
- + SFP_PHYS_EXT_ID = 0x01,
- + SFP_CONNECTOR = 0x02,
- + SFP_COMPLIANCE = 0x03,
- + SFP_ENCODING = 0x0b,
- + SFP_BR_NOMINAL = 0x0c,
- + SFP_RATE_ID = 0x0d,
- + SFP_LINK_LEN_SM_KM = 0x0e,
- + SFP_LINK_LEN_SM_100M = 0x0f,
- + SFP_LINK_LEN_50UM_OM2_10M = 0x10,
- + SFP_LINK_LEN_62_5UM_OM1_10M = 0x11,
- + SFP_LINK_LEN_COPPER_1M = 0x12,
- + SFP_LINK_LEN_50UM_OM4_10M = 0x12,
- + SFP_LINK_LEN_50UM_OM3_10M = 0x13,
- + SFP_VENDOR_NAME = 0x14,
- + SFP_VENDOR_OUI = 0x25,
- + SFP_VENDOR_PN = 0x28,
- + SFP_VENDOR_REV = 0x38,
- + SFP_OPTICAL_WAVELENGTH_MSB = 0x3c,
- + SFP_OPTICAL_WAVELENGTH_LSB = 0x3d,
- + SFP_CABLE_SPEC = 0x3c,
- + SFP_CC_BASE = 0x3f,
- + SFP_OPTIONS = 0x40, /* 2 bytes, MSB, LSB */
- + SFP_BR_MAX = 0x42,
- + SFP_BR_MIN = 0x43,
- + SFP_VENDOR_SN = 0x44,
- + SFP_DATECODE = 0x54,
- + SFP_DIAGMON = 0x5c,
- + SFP_ENHOPTS = 0x5d,
- + SFP_SFF8472_COMPLIANCE = 0x5e,
- + SFP_CC_EXT = 0x5f,
- +
- + SFP_PHYS_ID_SFP = 0x03,
- + SFP_PHYS_EXT_ID_SFP = 0x04,
- + SFP_CONNECTOR_UNSPEC = 0x00,
- + /* codes 01-05 not supportable on SFP, but some modules have single SC */
- + SFP_CONNECTOR_SC = 0x01,
- + SFP_CONNECTOR_FIBERJACK = 0x06,
- + SFP_CONNECTOR_LC = 0x07,
- + SFP_CONNECTOR_MT_RJ = 0x08,
- + SFP_CONNECTOR_MU = 0x09,
- + SFP_CONNECTOR_SG = 0x0a,
- + SFP_CONNECTOR_OPTICAL_PIGTAIL = 0x0b,
- + SFP_CONNECTOR_HSSDC_II = 0x20,
- + SFP_CONNECTOR_COPPER_PIGTAIL = 0x21,
- + SFP_ENCODING_UNSPEC = 0x00,
- + SFP_ENCODING_8B10B = 0x01,
- + SFP_ENCODING_4B5B = 0x02,
- + SFP_ENCODING_NRZ = 0x03,
- + SFP_ENCODING_MANCHESTER = 0x04,
- + SFP_OPTIONS_HIGH_POWER_LEVEL = BIT(13),
- + SFP_OPTIONS_PAGING_A2 = BIT(12),
- + SFP_OPTIONS_RETIMER = BIT(11),
- + SFP_OPTIONS_COOLED_XCVR = BIT(10),
- + SFP_OPTIONS_POWER_DECL = BIT(9),
- + SFP_OPTIONS_RX_LINEAR_OUT = BIT(8),
- + SFP_OPTIONS_RX_DECISION_THRESH = BIT(7),
- + SFP_OPTIONS_TUNABLE_TX = BIT(6),
- + SFP_OPTIONS_RATE_SELECT = BIT(5),
- + SFP_OPTIONS_TX_DISABLE = BIT(4),
- + SFP_OPTIONS_TX_FAULT = BIT(3),
- + SFP_OPTIONS_LOS_INVERTED = BIT(2),
- + SFP_OPTIONS_LOS_NORMAL = BIT(1),
- + SFP_DIAGMON_DDM = BIT(6),
- + SFP_DIAGMON_INT_CAL = BIT(5),
- + SFP_DIAGMON_EXT_CAL = BIT(4),
- + SFP_DIAGMON_RXPWR_AVG = BIT(3),
- + SFP_DIAGMON_ADDRMODE = BIT(2),
- + SFP_ENHOPTS_ALARMWARN = BIT(7),
- + SFP_ENHOPTS_SOFT_TX_DISABLE = BIT(6),
- + SFP_ENHOPTS_SOFT_TX_FAULT = BIT(5),
- + SFP_ENHOPTS_SOFT_RX_LOS = BIT(4),
- + SFP_ENHOPTS_SOFT_RATE_SELECT = BIT(3),
- + SFP_ENHOPTS_APP_SELECT_SFF8079 = BIT(2),
- + SFP_ENHOPTS_SOFT_RATE_SFF8431 = BIT(1),
- + SFP_SFF8472_COMPLIANCE_NONE = 0x00,
- + SFP_SFF8472_COMPLIANCE_REV9_3 = 0x01,
- + SFP_SFF8472_COMPLIANCE_REV9_5 = 0x02,
- + SFP_SFF8472_COMPLIANCE_REV10_2 = 0x03,
- + SFP_SFF8472_COMPLIANCE_REV10_4 = 0x04,
- + SFP_SFF8472_COMPLIANCE_REV11_0 = 0x05,
- + SFP_SFF8472_COMPLIANCE_REV11_3 = 0x06,
- + SFP_SFF8472_COMPLIANCE_REV11_4 = 0x07,
- + SFP_SFF8472_COMPLIANCE_REV12_0 = 0x08,
- +};
- +
- +/* SFP Diagnostics */
- +enum {
- + /* Alarm and warnings stored MSB at lower address then LSB */
- + SFP_TEMP_HIGH_ALARM = 0x00,
- + SFP_TEMP_LOW_ALARM = 0x02,
- + SFP_TEMP_HIGH_WARN = 0x04,
- + SFP_TEMP_LOW_WARN = 0x06,
- + SFP_VOLT_HIGH_ALARM = 0x08,
- + SFP_VOLT_LOW_ALARM = 0x0a,
- + SFP_VOLT_HIGH_WARN = 0x0c,
- + SFP_VOLT_LOW_WARN = 0x0e,
- + SFP_BIAS_HIGH_ALARM = 0x10,
- + SFP_BIAS_LOW_ALARM = 0x12,
- + SFP_BIAS_HIGH_WARN = 0x14,
- + SFP_BIAS_LOW_WARN = 0x16,
- + SFP_TXPWR_HIGH_ALARM = 0x18,
- + SFP_TXPWR_LOW_ALARM = 0x1a,
- + SFP_TXPWR_HIGH_WARN = 0x1c,
- + SFP_TXPWR_LOW_WARN = 0x1e,
- + SFP_RXPWR_HIGH_ALARM = 0x20,
- + SFP_RXPWR_LOW_ALARM = 0x22,
- + SFP_RXPWR_HIGH_WARN = 0x24,
- + SFP_RXPWR_LOW_WARN = 0x26,
- + SFP_LASER_TEMP_HIGH_ALARM = 0x28,
- + SFP_LASER_TEMP_LOW_ALARM = 0x2a,
- + SFP_LASER_TEMP_HIGH_WARN = 0x2c,
- + SFP_LASER_TEMP_LOW_WARN = 0x2e,
- + SFP_TEC_CUR_HIGH_ALARM = 0x30,
- + SFP_TEC_CUR_LOW_ALARM = 0x32,
- + SFP_TEC_CUR_HIGH_WARN = 0x34,
- + SFP_TEC_CUR_LOW_WARN = 0x36,
- + SFP_CAL_RXPWR4 = 0x38,
- + SFP_CAL_RXPWR3 = 0x3c,
- + SFP_CAL_RXPWR2 = 0x40,
- + SFP_CAL_RXPWR1 = 0x44,
- + SFP_CAL_RXPWR0 = 0x48,
- + SFP_CAL_TXI_SLOPE = 0x4c,
- + SFP_CAL_TXI_OFFSET = 0x4e,
- + SFP_CAL_TXPWR_SLOPE = 0x50,
- + SFP_CAL_TXPWR_OFFSET = 0x52,
- + SFP_CAL_T_SLOPE = 0x54,
- + SFP_CAL_T_OFFSET = 0x56,
- + SFP_CAL_V_SLOPE = 0x58,
- + SFP_CAL_V_OFFSET = 0x5a,
- + SFP_CHKSUM = 0x5f,
- +
- + SFP_TEMP = 0x60,
- + SFP_VCC = 0x62,
- + SFP_TX_BIAS = 0x64,
- + SFP_TX_POWER = 0x66,
- + SFP_RX_POWER = 0x68,
- + SFP_LASER_TEMP = 0x6a,
- + SFP_TEC_CUR = 0x6c,
- +
- + SFP_STATUS = 0x6e,
- + SFP_ALARM = 0x70,
- +
- + SFP_EXT_STATUS = 0x76,
- + SFP_VSL = 0x78,
- + SFP_PAGE = 0x7f,
- +};
- +
- +#endif
|