[PATCH 2.6] RTL8169 Suspend and Resume Stuff

From: Fernando Alencar Maróstica
Date: Sun Nov 30 2003 - 10:20:46 EST


Hi Jeff,

Here's a short patch for the new Realtek RTL8169 PCI Gigabit Driver
(drivers/net/r8169.c) in kernel. This patch add new PCI suspend
and resume code.

The patch applies against 2.4.23 and 2.6.0-test11. I've tested the
compilation with 2.4.23.

Happy Hack!

--
Fernando Alencar Maróstica
Graduate Student, Computer Science
Linux Register User Id #281457

University Methodist of Piracicaba
Departament of Computer Science
email: famarost@xxxxxxxxx
homepage: http://www.unimep.br/~famarost

--- r8169.c.orig 2003-11-30 12:01:19.000000000 -0200
+++ r8169.c 2003-11-30 12:39:55.000000000 -0200
@@ -33,6 +33,10 @@
- Copy mc_filter setup code from 8139cp
(includes an optimization, and avoids set_bit use)

+ <2003/11/30>
+
+ - Add new rtl8169_resume() support
+ - Add new rtl8169_suspend() support
*/

#include <linux/module.h>
@@ -359,6 +363,7 @@
int rc, i;
unsigned long mmio_start, mmio_end, mmio_flags, mmio_len;
u32 tmp;
+ int acpi_idle_state = 0, pm_cap;

assert(pdev != NULL);
assert(ioaddr_out != NULL);
@@ -366,10 +371,10 @@
*ioaddr_out = NULL;
*dev_out = NULL;

- // dev zeroed in alloc_etherdev
+ /* dev zeroed in alloc_etherdev */
dev = alloc_etherdev(sizeof (*tp));
- if (dev == NULL) {
- printk(KERN_ERR PFX "unable to alloc new ethernet\n");
+ if (!dev) {
+ printk(KERN_ERR PFX "%s: Could not allocate new ethernet device.\n", pdev->slot_name);
return -ENOMEM;
}

@@ -377,10 +382,24 @@
SET_NETDEV_DEV(dev, &pdev->dev);
tp = dev->priv;

- // enable device (incl. PCI PM wakeup and hotplug setup)
+ /* enable device (incl. PCI PM wakeup and hotplug setup) */
rc = pci_enable_device(pdev);
- if (rc)
+ if (rc) {
+ printk(KERN_ERR PFX "%s: unable to enable device\n", pdev->slot_name);
goto err_out;
+ }
+
+ /* save power state before pci_enable_device overwrites it */
+ pm_cap = pci_find_capability(pdev, PCI_CAP_ID_PM);
+ if (pm_cap) {
+ u16 pwr_command;
+ pci_read_config_word(pdev, pm_cap + PCI_PM_CTRL, &pwr_command);
+ acpi_idle_state = pwr_command & PCI_PM_CTRL_STATE_MASK;
+ }
+ else {
+ printk(KERN_ERR PFX "Cannot find PowerManagement capability, aborting.\n");
+ goto err_out_free_res;
+ }

mmio_start = pci_resource_start(pdev, 1);
mmio_end = pci_resource_end(pdev, 1);
@@ -402,10 +421,12 @@
}

rc = pci_request_regions(pdev, dev->name);
- if (rc)
+ if (rc) {
+ printk(KERN_ERR PFX "%s: Could not request regions.\n", pdev->slot_name);
goto err_out_disable;
+ }

- // enable PCI bus-mastering
+ /* enable PCI bus-mastering */
pci_set_master(pdev);

// ioremap MMIO region
@@ -466,7 +487,6 @@
struct rtl8169_private *tp = NULL;
void *ioaddr = NULL;
static int board_idx = -1;
- static int printed_version = 0;
int i, rc;
int option = -1, Cap10_100 = 0, Cap1000 = 0;

@@ -475,11 +495,16 @@

board_idx++;

- if (!printed_version) {
- printk(KERN_INFO RTL8169_DRIVER_NAME " loaded\n");
- printed_version = 1;
- }
++#ifndef MODULE
+ {
+ /* when built-in, we only print version if device is found */
+ static int did_version;

+ if (!did_version++)
+ printk(KERN_INFO RTL8169_DRIVER_NAME " loaded\n");
+ }
++#endif
+
rc = rtl8169_init_board(pdev, &dev, &ioaddr);
if (rc)
return rc;
@@ -651,6 +676,50 @@
pci_set_drvdata(pdev, NULL);
}

+#ifdef CONFIG_PM
+
+static int rtl8169_suspend (struct pci_dev *pci_device, u32 state)
+{
+ struct net_device *ethernet_device = pci_get_drvdata (pci_device);
+ struct rtl8169_private *tp = (struct rtl8169_private *) (ethernet_device->priv);
+ void *ioaddr = tp->mmio_addr;
+ unsigned long flags;
+
+ if (!netif_running (ethernet_device))
+ return 0;
+
+ netif_device_detach (ethernet_device);
+ netif_stop_queue (ethernet_device);
+ spin_lock_irqsave (&tp->lock, flags);
+
+ /* Disable interrupts, stop Rx and Tx */
+ writew ((0), ioaddr + (IntrMask));
+ writeb ((0), ioaddr + (ChipCmd));
+
+ /* Update the error counts. */
+ tp->stats.rx_missed_errors += RTL_R32 (RxMissed);
+ writel ((0), ioaddr + (RxMissed));
+ spin_unlock_irqrestore (&tp->lock, flags);
+
+ return 0;
+}
+
+static int rtl8169_resume (struct pci_dev *pci_device)
+{
+ struct net_device *ethernet_device = pci_get_drvdata (pci_device);
+
+ if (!netif_running (ethernet_device))
+ return 0;
+
+ netif_device_attach (ethernet_device);
+ rtl8169_hw_start (ethernet_device);
+ netif_start_queue (ethernet_device);
+
+ return 0;
+}
+
+#endif /* CONFIG_PM */
+
static int
rtl8169_open(struct net_device *dev)
{
@@ -698,6 +767,7 @@

rtl8169_init_ring(dev);
rtl8169_hw_start(dev);
+ netif_start_queue(dev);

return 0;

@@ -754,9 +824,6 @@

/* Enable all known interrupts by setting the interrupt mask. */
RTL_W16(IntrMask, rtl8169_intr_mask);
-
- netif_start_queue(dev);
-
}

static void
@@ -1126,8 +1193,10 @@
.id_table = rtl8169_pci_tbl,
.probe = rtl8169_init_one,
.remove = __devexit_p(rtl8169_remove_one),
- .suspend = NULL,
- .resume = NULL,
+#ifdef CONFIG_PM
+ .suspend = rtl8169_suspend,
+ .resume = rtl8169_resume,
+#endif
};

static int __init