net: sfp: soft status and control support
authorRussell King <rmk+kernel@armlinux.org.uk>
Wed, 20 Nov 2019 12:29:59 +0000 (12:29 +0000)
committerDavid S. Miller <davem@davemloft.net>
Thu, 21 Nov 2019 06:29:40 +0000 (22:29 -0800)
Add support for the soft status and control register, which allows
TX_FAULT and RX_LOS to be monitored and TX_DISABLE to be set.  We
make use of this when the board does not support GPIOs for these
signals.

Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk>
Reviewed-by: Andrew Lunn <andrew@lunn.ch>
Reviewed-by: Florian Fainelli <f.fainelli@gmail.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
drivers/net/phy/sfp.c
include/linux/sfp.h

index b0f88c2..bdbbb76 100644 (file)
@@ -201,7 +201,10 @@ struct sfp {
        struct gpio_desc *gpio[GPIO_MAX];
        int gpio_irq[GPIO_MAX];
 
+       bool need_poll;
+
        struct mutex st_mutex;                  /* Protects state */
+       unsigned int state_soft_mask;
        unsigned int state;
        struct delayed_work poll;
        struct delayed_work timeout;
@@ -395,24 +398,90 @@ static int sfp_i2c_configure(struct sfp *sfp, struct i2c_adapter *i2c)
 }
 
 /* Interface */
-static unsigned int sfp_get_state(struct sfp *sfp)
+static int sfp_read(struct sfp *sfp, bool a2, u8 addr, void *buf, size_t len)
 {
-       return sfp->get_state(sfp);
+       return sfp->read(sfp, a2, addr, buf, len);
 }
 
-static void sfp_set_state(struct sfp *sfp, unsigned int state)
+static int sfp_write(struct sfp *sfp, bool a2, u8 addr, void *buf, size_t len)
 {
-       sfp->set_state(sfp, state);
+       return sfp->write(sfp, a2, addr, buf, len);
 }
 
-static int sfp_read(struct sfp *sfp, bool a2, u8 addr, void *buf, size_t len)
+static unsigned int sfp_soft_get_state(struct sfp *sfp)
 {
-       return sfp->read(sfp, a2, addr, buf, len);
+       unsigned int state = 0;
+       u8 status;
+
+       if (sfp_read(sfp, true, SFP_STATUS, &status, sizeof(status)) ==
+                    sizeof(status)) {
+               if (status & SFP_STATUS_RX_LOS)
+                       state |= SFP_F_LOS;
+               if (status & SFP_STATUS_TX_FAULT)
+                       state |= SFP_F_TX_FAULT;
+       }
+
+       return state & sfp->state_soft_mask;
 }
 
-static int sfp_write(struct sfp *sfp, bool a2, u8 addr, void *buf, size_t len)
+static void sfp_soft_set_state(struct sfp *sfp, unsigned int state)
 {
-       return sfp->write(sfp, a2, addr, buf, len);
+       u8 status;
+
+       if (sfp_read(sfp, true, SFP_STATUS, &status, sizeof(status)) ==
+                    sizeof(status)) {
+               if (state & SFP_F_TX_DISABLE)
+                       status |= SFP_STATUS_TX_DISABLE_FORCE;
+               else
+                       status &= ~SFP_STATUS_TX_DISABLE_FORCE;
+
+               sfp_write(sfp, true, SFP_STATUS, &status, sizeof(status));
+       }
+}
+
+static void sfp_soft_start_poll(struct sfp *sfp)
+{
+       const struct sfp_eeprom_id *id = &sfp->id;
+
+       sfp->state_soft_mask = 0;
+       if (id->ext.enhopts & SFP_ENHOPTS_SOFT_TX_DISABLE &&
+           !sfp->gpio[GPIO_TX_DISABLE])
+               sfp->state_soft_mask |= SFP_F_TX_DISABLE;
+       if (id->ext.enhopts & SFP_ENHOPTS_SOFT_TX_FAULT &&
+           !sfp->gpio[GPIO_TX_FAULT])
+               sfp->state_soft_mask |= SFP_F_TX_FAULT;
+       if (id->ext.enhopts & SFP_ENHOPTS_SOFT_RX_LOS &&
+           !sfp->gpio[GPIO_LOS])
+               sfp->state_soft_mask |= SFP_F_LOS;
+
+       if (sfp->state_soft_mask & (SFP_F_LOS | SFP_F_TX_FAULT) &&
+           !sfp->need_poll)
+               mod_delayed_work(system_wq, &sfp->poll, poll_jiffies);
+}
+
+static void sfp_soft_stop_poll(struct sfp *sfp)
+{
+       sfp->state_soft_mask = 0;
+}
+
+static unsigned int sfp_get_state(struct sfp *sfp)
+{
+       unsigned int state = sfp->get_state(sfp);
+
+       if (state & SFP_F_PRESENT &&
+           sfp->state_soft_mask & (SFP_F_LOS | SFP_F_TX_FAULT))
+               state |= sfp_soft_get_state(sfp);
+
+       return state;
+}
+
+static void sfp_set_state(struct sfp *sfp, unsigned int state)
+{
+       sfp->set_state(sfp, state);
+
+       if (state & SFP_F_PRESENT &&
+           sfp->state_soft_mask & SFP_F_TX_DISABLE)
+               sfp_soft_set_state(sfp, state);
 }
 
 static unsigned int sfp_check(void *buf, size_t len)
@@ -1407,11 +1476,6 @@ static void sfp_sm_fault(struct sfp *sfp, unsigned int next_state, bool warn)
        }
 }
 
-static void sfp_sm_mod_init(struct sfp *sfp)
-{
-       sfp_module_tx_enable(sfp);
-}
-
 static void sfp_sm_probe_for_phy(struct sfp *sfp)
 {
        /* Setting the serdes link mode is guesswork: there's no
@@ -1574,7 +1638,7 @@ static int sfp_sm_mod_probe(struct sfp *sfp, bool report)
                 (int)sizeof(id.ext.datecode), id.ext.datecode);
 
        /* Check whether we support this module */
-       if (!sfp->type->module_supported(&sfp->id)) {
+       if (!sfp->type->module_supported(&id)) {
                dev_err(sfp->dev,
                        "module is not supported - phys id 0x%02x 0x%02x\n",
                        sfp->id.base.phys_id, sfp->id.base.phys_ext_id);
@@ -1764,6 +1828,7 @@ static void sfp_sm_main(struct sfp *sfp, unsigned int event)
                if (sfp->mod_phy)
                        sfp_sm_phy_detach(sfp);
                sfp_module_tx_disable(sfp);
+               sfp_soft_stop_poll(sfp);
                sfp_sm_next(sfp, SFP_S_DOWN, 0);
                return;
        }
@@ -1775,7 +1840,10 @@ static void sfp_sm_main(struct sfp *sfp, unsigned int event)
                    sfp->sm_dev_state != SFP_DEV_UP)
                        break;
 
-               sfp_sm_mod_init(sfp);
+               if (!(sfp->id.ext.diagmon & SFP_DIAGMON_ADDRMODE))
+                       sfp_soft_start_poll(sfp);
+
+               sfp_module_tx_enable(sfp);
 
                /* Initialise the fault clearance retries */
                sfp->sm_retries = 5;
@@ -2031,7 +2099,10 @@ 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);
+
+       if (sfp->state_soft_mask & (SFP_F_LOS | SFP_F_TX_FAULT) ||
+           sfp->need_poll)
+               mod_delayed_work(system_wq, &sfp->poll, poll_jiffies);
 }
 
 static struct sfp *sfp_alloc(struct device *dev)
@@ -2076,7 +2147,6 @@ static int sfp_probe(struct platform_device *pdev)
        const struct sff_data *sff;
        struct i2c_adapter *i2c;
        struct sfp *sfp;
-       bool poll = false;
        int err, i;
 
        sfp = sfp_alloc(&pdev->dev);
@@ -2183,7 +2253,7 @@ static int sfp_probe(struct platform_device *pdev)
 
                sfp->gpio_irq[i] = gpiod_to_irq(sfp->gpio[i]);
                if (!sfp->gpio_irq[i]) {
-                       poll = true;
+                       sfp->need_poll = true;
                        continue;
                }
 
@@ -2195,11 +2265,11 @@ static int sfp_probe(struct platform_device *pdev)
                                                dev_name(sfp->dev), sfp);
                if (err) {
                        sfp->gpio_irq[i] = 0;
-                       poll = true;
+                       sfp->need_poll = true;
                }
        }
 
-       if (poll)
+       if (sfp->need_poll)
                mod_delayed_work(system_wq, &sfp->poll, poll_jiffies);
 
        /* We could have an issue in cases no Tx disable pin is available or
index 3b35efd..487fd94 100644 (file)
@@ -428,6 +428,10 @@ enum {
        SFP_TEC_CUR                     = 0x6c,
 
        SFP_STATUS                      = 0x6e,
+       SFP_STATUS_TX_DISABLE           = BIT(7),
+       SFP_STATUS_TX_DISABLE_FORCE     = BIT(6),
+       SFP_STATUS_TX_FAULT             = BIT(2),
+       SFP_STATUS_RX_LOS               = BIT(1),
        SFP_ALARM0                      = 0x70,
        SFP_ALARM0_TEMP_HIGH            = BIT(7),
        SFP_ALARM0_TEMP_LOW             = BIT(6),