| 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 |
| @@ -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 |
| } |
| |
| /* 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 |
| } |
| } |
| |
| -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 * |
| (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, |
| 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, |
| 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 |
| 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_dev |
| const struct sff_data *sff; |
| struct i2c_adapter *i2c; |
| struct sfp *sfp; |
| - bool poll = false; |
| int err, i; |
| |
| sfp = sfp_alloc(&pdev->dev); |
| @@ -2184,7 +2254,7 @@ static int sfp_probe(struct platform_dev |
| sfp->gpio_irq[i] = gpiod_to_irq(sfp->gpio[i]); |
| if (sfp->gpio_irq[i] < 0) { |
| sfp->gpio_irq[i] = 0; |
| - poll = true; |
| + sfp->need_poll = true; |
| continue; |
| } |
| |
| @@ -2196,11 +2266,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), |