summaryrefslogtreecommitdiff
path: root/drivers/net/phy
diff options
context:
space:
mode:
authorRobert Hancock <hancock@sedsystems.ca>2019-06-07 10:42:35 -0600
committerDavid S. Miller <davem@davemloft.net>2019-06-09 19:25:59 -0700
commit257c25598584f9f7aaccbcd7b9a6faf23aec3156 (patch)
treecfbe3dd22ff0b75d7e06866517479619c720b97d /drivers/net/phy
parent5270041d342de6f1e6a3b6634c1ceaa67d1f87ea (diff)
net: sfp: Stop SFP polling and interrupt handling during shutdown
SFP device polling can cause problems during the shutdown process if the parent devices of the network controller have been shut down already. This problem was seen on the iMX6 platform with PCIe devices, where accessing the device after the bus is shut down causes a hang. Free any acquired GPIO interrupts and stop all delayed work in the SFP driver during the shutdown process, so that we ensure that no pending operations are still occurring after the SFP shutdown completes. Signed-off-by: Robert Hancock <hancock@sedsystems.ca> Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'drivers/net/phy')
-rw-r--r--drivers/net/phy/sfp.c31
1 files changed, 26 insertions, 5 deletions
diff --git a/drivers/net/phy/sfp.c b/drivers/net/phy/sfp.c
index a991c80e6567..44d787d36342 100644
--- a/drivers/net/phy/sfp.c
+++ b/drivers/net/phy/sfp.c
@@ -185,6 +185,7 @@ struct sfp {
int (*write)(struct sfp *, bool, u8, void *, size_t);
struct gpio_desc *gpio[GPIO_MAX];
+ int gpio_irq[GPIO_MAX];
bool attached;
unsigned int state;
@@ -1802,7 +1803,7 @@ static int sfp_probe(struct platform_device *pdev)
struct i2c_adapter *i2c;
struct sfp *sfp;
bool poll = false;
- int irq, err, i;
+ int err, i;
sfp = sfp_alloc(&pdev->dev);
if (IS_ERR(sfp))
@@ -1901,19 +1902,22 @@ static int sfp_probe(struct platform_device *pdev)
if (gpio_flags[i] != GPIOD_IN || !sfp->gpio[i])
continue;
- irq = gpiod_to_irq(sfp->gpio[i]);
- if (!irq) {
+ sfp->gpio_irq[i] = gpiod_to_irq(sfp->gpio[i]);
+ if (!sfp->gpio_irq[i]) {
poll = true;
continue;
}
- err = devm_request_threaded_irq(sfp->dev, irq, NULL, sfp_irq,
+ err = devm_request_threaded_irq(sfp->dev, sfp->gpio_irq[i],
+ NULL, sfp_irq,
IRQF_ONESHOT |
IRQF_TRIGGER_RISING |
IRQF_TRIGGER_FALLING,
dev_name(sfp->dev), sfp);
- if (err)
+ if (err) {
+ sfp->gpio_irq[i] = 0;
poll = true;
+ }
}
if (poll)
@@ -1944,9 +1948,26 @@ static int sfp_remove(struct platform_device *pdev)
return 0;
}
+static void sfp_shutdown(struct platform_device *pdev)
+{
+ struct sfp *sfp = platform_get_drvdata(pdev);
+ int i;
+
+ for (i = 0; i < GPIO_MAX; i++) {
+ if (!sfp->gpio_irq[i])
+ continue;
+
+ devm_free_irq(sfp->dev, sfp->gpio_irq[i], sfp);
+ }
+
+ cancel_delayed_work_sync(&sfp->poll);
+ cancel_delayed_work_sync(&sfp->timeout);
+}
+
static struct platform_driver sfp_driver = {
.probe = sfp_probe,
.remove = sfp_remove,
+ .shutdown = sfp_shutdown,
.driver = {
.name = "sfp",
.of_match_table = sfp_of_match,