226 lines
6.4 KiB
Diff
226 lines
6.4 KiB
Diff
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),
|