Commit f3c9a666 authored by Russell King's avatar Russell King Committed by David S. Miller

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: default avatarRussell King <rmk+kernel@armlinux.org.uk>
Reviewed-by: default avatarAndrew Lunn <andrew@lunn.ch>
Reviewed-by: default avatarFlorian Fainelli <f.fainelli@gmail.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 9ce33351
...@@ -201,7 +201,10 @@ struct sfp { ...@@ -201,7 +201,10 @@ struct sfp {
struct gpio_desc *gpio[GPIO_MAX]; struct gpio_desc *gpio[GPIO_MAX];
int gpio_irq[GPIO_MAX]; int gpio_irq[GPIO_MAX];
bool need_poll;
struct mutex st_mutex; /* Protects state */ struct mutex st_mutex; /* Protects state */
unsigned int state_soft_mask;
unsigned int state; unsigned int state;
struct delayed_work poll; struct delayed_work poll;
struct delayed_work timeout; struct delayed_work timeout;
...@@ -395,24 +398,90 @@ static int sfp_i2c_configure(struct sfp *sfp, struct i2c_adapter *i2c) ...@@ -395,24 +398,90 @@ static int sfp_i2c_configure(struct sfp *sfp, struct i2c_adapter *i2c)
} }
/* Interface */ /* 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) 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) ...@@ -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) static void sfp_sm_probe_for_phy(struct sfp *sfp)
{ {
/* Setting the serdes link mode is guesswork: there's no /* 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) ...@@ -1574,7 +1638,7 @@ static int sfp_sm_mod_probe(struct sfp *sfp, bool report)
(int)sizeof(id.ext.datecode), id.ext.datecode); (int)sizeof(id.ext.datecode), id.ext.datecode);
/* Check whether we support this module */ /* Check whether we support this module */
if (!sfp->type->module_supported(&sfp->id)) { if (!sfp->type->module_supported(&id)) {
dev_err(sfp->dev, dev_err(sfp->dev,
"module is not supported - phys id 0x%02x 0x%02x\n", "module is not supported - phys id 0x%02x 0x%02x\n",
sfp->id.base.phys_id, sfp->id.base.phys_ext_id); 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) ...@@ -1764,6 +1828,7 @@ static void sfp_sm_main(struct sfp *sfp, unsigned int event)
if (sfp->mod_phy) if (sfp->mod_phy)
sfp_sm_phy_detach(sfp); sfp_sm_phy_detach(sfp);
sfp_module_tx_disable(sfp); sfp_module_tx_disable(sfp);
sfp_soft_stop_poll(sfp);
sfp_sm_next(sfp, SFP_S_DOWN, 0); sfp_sm_next(sfp, SFP_S_DOWN, 0);
return; return;
} }
...@@ -1775,7 +1840,10 @@ static void sfp_sm_main(struct sfp *sfp, unsigned int event) ...@@ -1775,7 +1840,10 @@ static void sfp_sm_main(struct sfp *sfp, unsigned int event)
sfp->sm_dev_state != SFP_DEV_UP) sfp->sm_dev_state != SFP_DEV_UP)
break; 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 */ /* Initialise the fault clearance retries */
sfp->sm_retries = 5; sfp->sm_retries = 5;
...@@ -2031,6 +2099,9 @@ static void sfp_poll(struct work_struct *work) ...@@ -2031,6 +2099,9 @@ static void sfp_poll(struct work_struct *work)
struct sfp *sfp = container_of(work, struct sfp, poll.work); struct sfp *sfp = container_of(work, struct sfp, poll.work);
sfp_check_state(sfp); sfp_check_state(sfp);
if (sfp->state_soft_mask & (SFP_F_LOS | SFP_F_TX_FAULT) ||
sfp->need_poll)
mod_delayed_work(system_wq, &sfp->poll, poll_jiffies); mod_delayed_work(system_wq, &sfp->poll, poll_jiffies);
} }
...@@ -2076,7 +2147,6 @@ static int sfp_probe(struct platform_device *pdev) ...@@ -2076,7 +2147,6 @@ static int sfp_probe(struct platform_device *pdev)
const struct sff_data *sff; const struct sff_data *sff;
struct i2c_adapter *i2c; struct i2c_adapter *i2c;
struct sfp *sfp; struct sfp *sfp;
bool poll = false;
int err, i; int err, i;
sfp = sfp_alloc(&pdev->dev); sfp = sfp_alloc(&pdev->dev);
...@@ -2183,7 +2253,7 @@ static int sfp_probe(struct platform_device *pdev) ...@@ -2183,7 +2253,7 @@ static int sfp_probe(struct platform_device *pdev)
sfp->gpio_irq[i] = gpiod_to_irq(sfp->gpio[i]); sfp->gpio_irq[i] = gpiod_to_irq(sfp->gpio[i]);
if (!sfp->gpio_irq[i]) { if (!sfp->gpio_irq[i]) {
poll = true; sfp->need_poll = true;
continue; continue;
} }
...@@ -2195,11 +2265,11 @@ static int sfp_probe(struct platform_device *pdev) ...@@ -2195,11 +2265,11 @@ static int sfp_probe(struct platform_device *pdev)
dev_name(sfp->dev), sfp); dev_name(sfp->dev), sfp);
if (err) { if (err) {
sfp->gpio_irq[i] = 0; 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); mod_delayed_work(system_wq, &sfp->poll, poll_jiffies);
/* We could have an issue in cases no Tx disable pin is available or /* We could have an issue in cases no Tx disable pin is available or
......
...@@ -428,6 +428,10 @@ enum { ...@@ -428,6 +428,10 @@ enum {
SFP_TEC_CUR = 0x6c, SFP_TEC_CUR = 0x6c,
SFP_STATUS = 0x6e, 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 = 0x70,
SFP_ALARM0_TEMP_HIGH = BIT(7), SFP_ALARM0_TEMP_HIGH = BIT(7),
SFP_ALARM0_TEMP_LOW = BIT(6), SFP_ALARM0_TEMP_LOW = BIT(6),
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment