diff options
Diffstat (limited to 'target/linux/generic/backport-4.19/744-v5.5-net-sfp-soft-status-and-control-support.patch')
-rw-r--r-- | target/linux/generic/backport-4.19/744-v5.5-net-sfp-soft-status-and-control-support.patch | 225 |
1 files changed, 225 insertions, 0 deletions
diff --git a/target/linux/generic/backport-4.19/744-v5.5-net-sfp-soft-status-and-control-support.patch b/target/linux/generic/backport-4.19/744-v5.5-net-sfp-soft-status-and-control-support.patch new file mode 100644 index 0000000000..163d8072e6 --- /dev/null +++ b/target/linux/generic/backport-4.19/744-v5.5-net-sfp-soft-status-and-control-support.patch @@ -0,0 +1,225 @@ +From 40e0b3b15f7da92e6b065292b14af7b9bfb1c6e0 Mon Sep 17 00:00:00 2001 +From: Russell King <rmk+kernel@armlinux.org.uk> +Date: Fri, 13 Sep 2019 23:00:35 +0100 +Subject: [PATCH 642/660] net: sfp: soft status and control support + +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> +--- + drivers/net/phy/sfp.c | 110 ++++++++++++++++++++++++++++++++++-------- + include/linux/sfp.h | 4 ++ + 2 files changed, 94 insertions(+), 20 deletions(-) + +--- a/drivers/net/phy/sfp.c ++++ b/drivers/net/phy/sfp.c +@@ -199,7 +199,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; +@@ -393,24 +396,90 @@ static int sfp_i2c_configure(struct sfp + } + + /* 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) +@@ -1342,11 +1411,6 @@ static void sfp_sm_fault(struct sfp *sfp + } + } + +-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 +@@ -1509,7 +1573,7 @@ static int sfp_sm_mod_probe(struct sfp * + (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); +@@ -1699,6 +1763,7 @@ static void sfp_sm_main(struct sfp *sfp, + 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; + } +@@ -1710,7 +1775,10 @@ static void sfp_sm_main(struct sfp *sfp, + 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; +@@ -1966,7 +2034,10 @@ static void sfp_poll(struct work_struct + 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) +@@ -2010,7 +2081,6 @@ static int sfp_probe(struct platform_dev + { + const struct sff_data *sff; + struct sfp *sfp; +- bool poll = false; + int err, i; + + sfp = sfp_alloc(&pdev->dev); +@@ -2100,7 +2170,7 @@ static int sfp_probe(struct platform_dev + + sfp->gpio_irq[i] = gpiod_to_irq(sfp->gpio[i]); + if (!sfp->gpio_irq[i]) { +- poll = true; ++ sfp->need_poll = true; + continue; + } + +@@ -2112,11 +2182,11 @@ static int sfp_probe(struct platform_dev + 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 +--- a/include/linux/sfp.h ++++ b/include/linux/sfp.h +@@ -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), |