[PATCH] dl2k: Implement suspend
From: Ondrej Zary
Date: Mon Nov 16 2015 - 17:42:10 EST
Add suspend/resume support to dl2k driver.
Tested on Asus NX1101 (IP1000A) and D-Link DGE-550T (DL-2000).
Signed-off-by: Ondrej Zary <linux@xxxxxxxxxxxxxxxxxxxx>
---
drivers/net/ethernet/dlink/dl2k.c | 189 +++++++++++++++++++++++++------------
1 file changed, 128 insertions(+), 61 deletions(-)
diff --git a/drivers/net/ethernet/dlink/dl2k.c b/drivers/net/ethernet/dlink/dl2k.c
index ccca479..23d13c5 100644
--- a/drivers/net/ethernet/dlink/dl2k.c
+++ b/drivers/net/ethernet/dlink/dl2k.c
@@ -253,19 +253,6 @@ rio_probe1 (struct pci_dev *pdev, const struct pci_device_id *ent)
if (err)
goto err_out_unmap_rx;
- if (np->chip_id == CHIP_IP1000A &&
- (np->pdev->revision == 0x40 || np->pdev->revision == 0x41)) {
- /* PHY magic taken from ipg driver, undocumented registers */
- mii_write(dev, np->phy_addr, 31, 0x0001);
- mii_write(dev, np->phy_addr, 27, 0x01e0);
- mii_write(dev, np->phy_addr, 31, 0x0002);
- mii_write(dev, np->phy_addr, 27, 0xeb8e);
- mii_write(dev, np->phy_addr, 31, 0x0000);
- mii_write(dev, np->phy_addr, 30, 0x005e);
- /* advertise 1000BASE-T half & full duplex, prefer MASTER */
- mii_write(dev, np->phy_addr, MII_CTRL1000, 0x0700);
- }
-
/* Fiber device? */
np->phy_media = (dr16(ASICCtrl) & PhyMedia) ? 1 : 0;
np->link_status = 0;
@@ -275,13 +262,11 @@ rio_probe1 (struct pci_dev *pdev, const struct pci_device_id *ent)
if (np->an_enable == 2) {
np->an_enable = 1;
}
- mii_set_media_pcs (dev);
} else {
/* Auto-Negotiation is mandatory for 1000BASE-T,
IEEE 802.3ab Annex 28D page 14 */
if (np->speed == 1000)
np->an_enable = 1;
- mii_set_media (dev);
}
err = register_netdev (dev);
@@ -446,19 +431,13 @@ static void rio_set_led_mode(struct net_device *dev)
dw32(ASICCtrl, mode);
}
-static int
-rio_open (struct net_device *dev)
+static void rio_hw_init(struct net_device *dev)
{
struct netdev_private *np = netdev_priv(dev);
void __iomem *ioaddr = np->ioaddr;
- const int irq = np->pdev->irq;
int i;
u16 macctrl;
- i = request_irq(irq, rio_interrupt, IRQF_SHARED, dev->name, dev);
- if (i)
- return i;
-
/* Reset all logic functions */
dw16(ASICCtrl + 2,
GlobalReset | DMAReset | FIFOReset | NetworkReset | HostReset);
@@ -469,11 +448,31 @@ rio_open (struct net_device *dev)
/* DebugCtrl bit 4, 5, 9 must set */
dw32(DebugCtrl, dr32(DebugCtrl) | 0x0230);
+ if (np->chip_id == CHIP_IP1000A &&
+ (np->pdev->revision == 0x40 || np->pdev->revision == 0x41)) {
+ /* PHY magic taken from ipg driver, undocumented registers */
+ mii_write(dev, np->phy_addr, 31, 0x0001);
+ mii_write(dev, np->phy_addr, 27, 0x01e0);
+ mii_write(dev, np->phy_addr, 31, 0x0002);
+ mii_write(dev, np->phy_addr, 27, 0xeb8e);
+ mii_write(dev, np->phy_addr, 31, 0x0000);
+ mii_write(dev, np->phy_addr, 30, 0x005e);
+ /* advertise 1000BASE-T half & full duplex, prefer MASTER */
+ mii_write(dev, np->phy_addr, MII_CTRL1000, 0x0700);
+ }
+
+ if (np->phy_media)
+ mii_set_media_pcs(dev);
+ else
+ mii_set_media(dev);
+
/* Jumbo frame */
if (np->jumbo != 0)
dw16(MaxFrameSize, MAX_JUMBO+14);
- alloc_list (dev);
+ /* Set RFDListPtr */
+ dw32(RFDListPtr0, np->rx_ring_dma);
+ dw32(RFDListPtr1, 0);
/* Set station address */
/* 16 or 32-bit access is required by TC9020 datasheet but 8-bit works
@@ -493,8 +492,6 @@ rio_open (struct net_device *dev)
dw8(RxDMABurstThresh, 0x30);
dw8(RxDMAUrgentThresh, 0x30);
dw32(RmonStatMask, 0x0007ffff);
- /* clear statistics */
- clear_stats (dev);
/* VLAN supported */
if (np->vlan) {
@@ -509,10 +506,6 @@ rio_open (struct net_device *dev)
dw32(MACCtrl, dr32(MACCtrl) | AutoVLANuntagging);
}
- setup_timer(&np->timer, rio_timer, (unsigned long)dev);
- np->timer.expires = jiffies + 1*HZ;
- add_timer (&np->timer);
-
/* Start Tx/Rx */
dw32(MACCtrl, dr32(MACCtrl) | StatsEnable | RxEnable | TxEnable);
@@ -522,6 +515,28 @@ rio_open (struct net_device *dev)
macctrl |= (np->tx_flow) ? TxFlowControlEnable : 0;
macctrl |= (np->rx_flow) ? RxFlowControlEnable : 0;
dw16(MACCtrl, macctrl);
+}
+
+static int rio_open(struct net_device *dev)
+{
+ struct netdev_private *np = netdev_priv(dev);
+ const int irq = np->pdev->irq;
+ int i;
+
+ i = request_irq(irq, rio_interrupt, IRQF_SHARED, dev->name, dev);
+ if (i)
+ return i;
+
+ alloc_list(dev);
+
+ rio_hw_init(dev);
+
+ /* clear statistics */
+ clear_stats(dev);
+
+ setup_timer(&np->timer, rio_timer, (unsigned long)dev);
+ np->timer.expires = jiffies + 1 * HZ;
+ add_timer(&np->timer);
netif_start_queue (dev);
@@ -591,7 +606,6 @@ static void
alloc_list (struct net_device *dev)
{
struct netdev_private *np = netdev_priv(dev);
- void __iomem *ioaddr = np->ioaddr;
int i;
np->cur_rx = np->cur_tx = 0;
@@ -634,10 +648,40 @@ alloc_list (struct net_device *dev)
PCI_DMA_FROMDEVICE));
np->rx_ring[i].fraginfo |= cpu_to_le64((u64)np->rx_buf_sz << 48);
}
+}
- /* Set RFDListPtr */
- dw32(RFDListPtr0, np->rx_ring_dma);
- dw32(RFDListPtr1, 0);
+static inline dma_addr_t desc_to_dma(struct netdev_desc *desc)
+{
+ return le64_to_cpu(desc->fraginfo) & DMA_BIT_MASK(48);
+}
+
+static void free_list(struct net_device *dev)
+{
+ struct netdev_private *np = netdev_priv(dev);
+ struct sk_buff *skb;
+ int i;
+
+ /* Free all the skbuffs in the queue. */
+ for (i = 0; i < RX_RING_SIZE; i++) {
+ skb = np->rx_skbuff[i];
+ if (skb) {
+ pci_unmap_single(np->pdev, desc_to_dma(&np->rx_ring[i]),
+ skb->len, PCI_DMA_FROMDEVICE);
+ dev_kfree_skb(skb);
+ np->rx_skbuff[i] = NULL;
+ }
+ np->rx_ring[i].status = 0;
+ np->rx_ring[i].fraginfo = 0;
+ }
+ for (i = 0; i < TX_RING_SIZE; i++) {
+ skb = np->tx_skbuff[i];
+ if (skb) {
+ pci_unmap_single(np->pdev, desc_to_dma(&np->tx_ring[i]),
+ skb->len, PCI_DMA_TODEVICE);
+ dev_kfree_skb(skb);
+ np->tx_skbuff[i] = NULL;
+ }
+ }
}
static netdev_tx_t
@@ -748,11 +792,6 @@ rio_interrupt (int irq, void *dev_instance)
return IRQ_RETVAL(handled);
}
-static inline dma_addr_t desc_to_dma(struct netdev_desc *desc)
-{
- return le64_to_cpu(desc->fraginfo) & DMA_BIT_MASK(48);
-}
-
static void
rio_free_tx (struct net_device *dev, int irq)
{
@@ -1733,8 +1772,6 @@ rio_close (struct net_device *dev)
void __iomem *ioaddr = np->ioaddr;
struct pci_dev *pdev = np->pdev;
- struct sk_buff *skb;
- int i;
netif_stop_queue (dev);
@@ -1747,27 +1784,7 @@ rio_close (struct net_device *dev)
free_irq(pdev->irq, dev);
del_timer_sync (&np->timer);
- /* Free all the skbuffs in the queue. */
- for (i = 0; i < RX_RING_SIZE; i++) {
- skb = np->rx_skbuff[i];
- if (skb) {
- pci_unmap_single(pdev, desc_to_dma(&np->rx_ring[i]),
- skb->len, PCI_DMA_FROMDEVICE);
- dev_kfree_skb (skb);
- np->rx_skbuff[i] = NULL;
- }
- np->rx_ring[i].status = 0;
- np->rx_ring[i].fraginfo = 0;
- }
- for (i = 0; i < TX_RING_SIZE; i++) {
- skb = np->tx_skbuff[i];
- if (skb) {
- pci_unmap_single(pdev, desc_to_dma(&np->tx_ring[i]),
- skb->len, PCI_DMA_TODEVICE);
- dev_kfree_skb (skb);
- np->tx_skbuff[i] = NULL;
- }
- }
+ free_list(dev);
return 0;
}
@@ -1795,11 +1812,61 @@ rio_remove1 (struct pci_dev *pdev)
}
}
+#ifdef CONFIG_PM
+static int rio_suspend(struct pci_dev *pdev, pm_message_t state)
+{
+ struct net_device *dev = pci_get_drvdata(pdev);
+ struct netdev_private *np = netdev_priv(dev);
+ void __iomem *ioaddr = np->ioaddr;
+
+ pci_save_state(pdev);
+
+ if (netif_running(dev)) {
+ netif_device_detach(dev);
+ del_timer_sync(&np->timer);
+ /* Disable interrupts */
+ dw16(IntEnable, 0);
+ /* Stop Tx and Rx logics */
+ dw32(MACCtrl, TxDisable | RxDisable | StatsDisable);
+ free_list(dev);
+ }
+
+ pci_set_power_state(pdev, PCI_D3hot);
+
+ return 0;
+}
+
+static int rio_resume(struct pci_dev *pdev)
+{
+ struct net_device *dev = pci_get_drvdata(pdev);
+ struct netdev_private *np = netdev_priv(dev);
+
+ pci_set_power_state(pdev, PCI_D0);
+ pci_restore_state(pdev);
+
+ if (netif_running(dev)) {
+ alloc_list(dev);
+ rio_hw_init(dev);
+ np->timer.expires = jiffies + 1 * HZ;
+ add_timer(&np->timer);
+ netif_device_attach(dev);
+ dl2k_enable_int(np);
+ }
+
+ return 0;
+}
+
+#endif /* CONFIG_PM */
+
static struct pci_driver rio_driver = {
.name = "dl2k",
.id_table = rio_pci_tbl,
.probe = rio_probe1,
.remove = rio_remove1,
+#ifdef CONFIG_PM
+ .suspend = rio_suspend,
+ .resume = rio_resume,
+#endif /* CONFIG_PM */
};
module_pci_driver(rio_driver);
--
Ondrej Zary
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/