* [PATCH] test patch, sungem wake-on-lan for 2.6.9+sleep
@ 2004-11-27 16:58 Colin Leroy
0 siblings, 0 replies; only message in thread
From: Colin Leroy @ 2004-11-27 16:58 UTC (permalink / raw)
To: debian-powerpc, linuxppc-dev
[-- Attachment #1: Type: text/plain, Size: 156 bytes --]
Hi,
this patch is a port of Ben's sungem WOL patch that applies on top of
2.6.9 + Ben's sleep patch. It also has 2.6.10-rc2's therm_adt746x.
hth
--
Colin
[-- Attachment #2: sleep_and_wol.patch --]
[-- Type: application/octet-stream, Size: 55465 bytes --]
diff -ur a/arch/ppc/platforms/pmac_feature.c b/arch/ppc/platforms/pmac_feature.c
--- a/arch/ppc/platforms/pmac_feature.c 2004-11-26 20:09:44.768528928 +0100
+++ b/arch/ppc/platforms/pmac_feature.c 2004-11-26 17:36:42.000000000 +0100
@@ -1688,8 +1688,11 @@
*/
save_unin_clock_ctl = UN_IN(UNI_N_CLOCK_CNTL);
+ /* Note: do not switch GMAC off, driver does it when necessary, WOL must keep it
+ * enabled !
+ */
UN_OUT(UNI_N_CLOCK_CNTL, save_unin_clock_ctl &
- ~(UNI_N_CLOCK_CNTL_GMAC|UNI_N_CLOCK_CNTL_FW/*|UNI_N_CLOCK_CNTL_PCI*/));
+ ~(/*UNI_N_CLOCK_CNTL_GMAC|*/UNI_N_CLOCK_CNTL_FW/*|UNI_N_CLOCK_CNTL_PCI*/));
udelay(100);
UN_OUT(UNI_N_HWINIT_STATE, UNI_N_HWINIT_STATE_SLEEPING);
UN_OUT(UNI_N_POWER_MGT, UNI_N_POWER_MGT_SLEEP);
diff -ur a/drivers/macintosh/therm_adt746x.c b/drivers/macintosh/therm_adt746x.c
--- a/drivers/macintosh/therm_adt746x.c 2004-10-18 23:54:54.000000000 +0200
+++ b/drivers/macintosh/therm_adt746x.c 2004-11-19 09:00:35.000000000 +0100
@@ -22,13 +22,16 @@
#include <linux/spinlock.h>
#include <linux/smp_lock.h>
#include <linux/wait.h>
+#include <linux/suspend.h>
+#include <linux/kthread.h>
+#include <linux/moduleparam.h>
+
#include <asm/prom.h>
#include <asm/machdep.h>
#include <asm/io.h>
#include <asm/system.h>
#include <asm/sections.h>
#include <asm/of_device.h>
-#include <linux/kthread.h>
#undef DEBUG
@@ -50,21 +53,26 @@
static int fan_speed = -1;
MODULE_AUTHOR("Colin Leroy <colin@colino.net>");
-MODULE_DESCRIPTION("Driver for ADT746x thermostat in iBook G4 and Powerbook G4 Alu");
+MODULE_DESCRIPTION("Driver for ADT746x thermostat in iBook G4 and "
+ "Powerbook G4 Alu");
MODULE_LICENSE("GPL");
-MODULE_PARM(limit_adjust,"i");
-MODULE_PARM_DESC(limit_adjust,"Adjust maximum temperatures (50 cpu, 70 gpu) by N degrees.");
-MODULE_PARM(fan_speed,"i");
-MODULE_PARM_DESC(fan_speed,"Specify fan speed (0-255) when lim < temp < lim+8 (default 128)");
+module_param(limit_adjust, int, 0644);
+MODULE_PARM_DESC(limit_adjust,"Adjust maximum temperatures (50 cpu, 70 gpu) "
+ "by N degrees.");
+
+module_param(fan_speed, int, 0644);
+MODULE_PARM_DESC(fan_speed,"Specify starting fan speed (0-255) "
+ "(default 64)");
struct thermostat {
struct i2c_client clt;
+ u8 temps[3];
u8 cached_temp[3];
u8 initial_limits[3];
u8 limits[3];
int last_speed[2];
- int overriding[2];
+ int last_var[2];
};
static enum {ADT7460, ADT7467} therm_type;
@@ -73,7 +81,9 @@
static struct thermostat* thermostat;
static struct task_struct *thread_therm = NULL;
-static int attach_one_thermostat(struct i2c_adapter *adapter, int addr, int busno);
+static int attach_one_thermostat(struct i2c_adapter *adapter, int addr,
+ int busno);
+
static void write_both_fan_speed(struct thermostat *th, int speed);
static void write_fan_speed(struct thermostat *th, int speed, int fan);
@@ -139,10 +149,11 @@
kthread_stop(thread_therm);
}
- printk(KERN_INFO "adt746x: Putting max temperatures back from %d, %d, %d,"
- " to %d, %d, %d\n",
+ printk(KERN_INFO "adt746x: Putting max temperatures back from "
+ "%d, %d, %d to %d, %d, %d\n",
th->limits[0], th->limits[1], th->limits[2],
- th->initial_limits[0], th->initial_limits[1], th->initial_limits[2]);
+ th->initial_limits[0], th->initial_limits[1],
+ th->initial_limits[2]);
for (i = 0; i < 3; i++)
write_reg(th, LIMIT_REG[i], th->initial_limits[i]);
@@ -201,11 +212,11 @@
if (th->last_speed[fan] != speed) {
if (speed == -1)
- printk(KERN_INFO "adt746x: Setting speed to: automatic for %s fan.\n",
- fan?"GPU":"CPU");
+ printk(KERN_DEBUG "adt746x: Setting speed to automatic "
+ "for %s fan.\n", fan?"GPU":"CPU");
else
- printk(KERN_INFO "adt746x: Setting speed to: %d for %s fan.\n",
- speed, fan?"GPU":"CPU");
+ printk(KERN_DEBUG "adt746x: Setting speed to %d "
+ "for %s fan.\n", speed, fan?"GPU":"CPU");
} else
return;
@@ -216,8 +227,11 @@
} else {
/* back to automatic */
if(therm_type == ADT7460) {
- manual = read_reg(th, MANUAL_MODE[fan]) & (~MANUAL_MASK);
- write_reg(th, MANUAL_MODE[fan], manual|REM_CONTROL[fan]);
+ manual = read_reg(th,
+ MANUAL_MODE[fan]) & (~MANUAL_MASK);
+
+ write_reg(th,
+ MANUAL_MODE[fan], manual|REM_CONTROL[fan]);
} else {
manual = read_reg(th, MANUAL_MODE[fan]);
write_reg(th, MANUAL_MODE[fan], manual&(~AUTO_MASK));
@@ -227,97 +241,117 @@
th->last_speed[fan] = speed;
}
-static int monitor_task(void *arg)
+static void read_sensors(struct thermostat *th)
{
- struct thermostat* th = arg;
- u8 temps[3];
- u8 lims[3];
- int i;
+ int i = 0;
+
+ for (i = 0; i < 3; i++)
+ th->temps[i] = read_reg(th, TEMP_REG[i]);
+}
+
#ifdef DEBUG
- int mfan_speed;
+static void display_stats(struct thermostat *th)
+{
+ if (th->temps[0] != th->cached_temp[0]
+ || th->temps[1] != th->cached_temp[1]
+ || th->temps[2] != th->cached_temp[2]) {
+ printk(KERN_INFO "adt746x: Temperature infos:"
+ " thermostats: %d,%d,%d;"
+ " limits: %d,%d,%d;"
+ " fan speed: %d RPM\n",
+ th->temps[0], th->temps[1], th->temps[2],
+ th->limits[0], th->limits[1], th->limits[2],
+ read_fan_speed(th, FAN_SPEED[0]));
+ }
+ th->cached_temp[0] = th->temps[0];
+ th->cached_temp[1] = th->temps[1];
+ th->cached_temp[2] = th->temps[2];
+}
#endif
- while(!kthread_should_stop())
- {
- msleep_interruptible(2000);
- /* Check status */
- /* local : chip */
- /* remote 1: CPU ?*/
- /* remote 2: GPU ?*/
-#ifndef DEBUG
- if (fan_speed != -1) {
-#endif
- for (i = 0; i < 3; i++) {
- temps[i] = read_reg(th, TEMP_REG[i]);
- lims[i] = th->limits[i];
+static void update_fans_speed (struct thermostat *th)
+{
+ int lastvar = 0; /* last variation, for iBook */
+ int i = 0;
+
+ /* we don't care about local sensor, so we start at sensor 1 */
+ for (i = 1; i < 3; i++) {
+ int started = 0;
+ int fan_number = (therm_type == ADT7460 && i == 2);
+ int var = th->temps[i] - th->limits[i];
+
+ if (var > -1) {
+ int step = (255 - fan_speed) / 7;
+ int new_speed = 0;
+
+ /* hysteresis : change fan speed only if variation is
+ * more than two degrees */
+ if (abs(var - th->last_var[fan_number]) < 2)
+ continue;
+
+ started = 1;
+ new_speed = fan_speed + ((var-1)*step);
+
+ if (new_speed < fan_speed)
+ new_speed = fan_speed;
+ if (new_speed > 255)
+ new_speed = 255;
+
+ printk(KERN_DEBUG "adt746x: setting fans speed to %d "
+ "(limit exceeded by %d on %s) \n",
+ new_speed, var,
+ fan_number?"GPU/pwr":"CPU");
+ write_both_fan_speed(th, new_speed);
+ th->last_var[fan_number] = var;
+ } else if (var < -2) {
+ /* don't stop fan if GPU/power is cold and CPU is not
+ * so cold (lastvar >= -1) */
+ if (i == 2 && lastvar < -1) {
+ if (th->last_speed[fan_number] != 0)
+ printk(KERN_DEBUG "adt746x: Stopping "
+ "fans.\n");
+ write_both_fan_speed(th, 0);
}
-#ifndef DEBUG
}
+
+ lastvar = var;
+
+ if (started)
+ return; /* we don't want to re-stop the fan
+ * if CPU is heating and GPU/power is not */
+ }
+}
+
+static int monitor_task(void *arg)
+{
+ struct thermostat* th = arg;
+
+ while(!kthread_should_stop()) {
+ if (current->flags & PF_FREEZE)
+ refrigerator(PF_FREEZE);
+
+ msleep_interruptible(2000);
+
+#ifndef DEBUG
+ if (fan_speed != -1)
+ read_sensors(th);
+#else
+ read_sensors(th);
#endif
- if (fan_speed != -1) {
- int lastvar = 0; /* for iBook */
- for (i = 1; i < 3; i++) { /* we don't care about local sensor */
- int started = 0;
- int fan_number = (therm_type == ADT7460 && i == 2);
- int var = temps[i] - lims[i];
- if (var > 8) {
- if (th->overriding[fan_number] == 0)
- printk(KERN_INFO "adt746x: Limit exceeded by %d, overriding specified fan speed for %s.\n",
- var, fan_number?"GPU":"CPU");
- th->overriding[fan_number] = 1;
- write_fan_speed(th, 255, fan_number);
- started = 1;
- } else if ((!th->overriding[fan_number] || var < 6) && var > 0) {
- if (th->overriding[fan_number] == 1)
- printk(KERN_INFO "adt746x: Limit exceeded by %d, setting speed to specified for %s.\n",
- var, fan_number?"GPU":"CPU");
- th->overriding[fan_number] = 0;
- write_fan_speed(th, fan_speed, fan_number);
- started = 1;
- } else if (var < -1) {
- /* don't stop iBook fan if GPU is cold and CPU is not
- * so cold (lastvar >= -1) */
- if (therm_type == ADT7460 || lastvar < -1 || i == 1) {
- if (th->last_speed[fan_number] != 0)
- printk(KERN_INFO "adt746x: Stopping %s fan.\n",
- fan_number?"GPU":"CPU");
- write_fan_speed(th, 0, fan_number);
- }
- }
-
- lastvar = var;
-
- if (started && therm_type == ADT7467)
- break; /* we don't want to re-stop the fan
- * if CPU is heating and GPU is not */
- }
- }
+
+ if (fan_speed != -1)
+ update_fans_speed(th);
+
#ifdef DEBUG
- mfan_speed = read_fan_speed(th, FAN_SPEED[0]);
- /* only one fan in the iBook G4 */
-
- if (temps[0] != th->cached_temp[0]
- || temps[1] != th->cached_temp[1]
- || temps[2] != th->cached_temp[2]) {
- printk(KERN_INFO "adt746x: Temperature infos:"
- " thermostats: %d,%d,%d;"
- " limits: %d,%d,%d;"
- " fan speed: %d RPM\n",
- temps[0], temps[1], temps[2],
- lims[0], lims[1], lims[2],
- mfan_speed);
- }
- th->cached_temp[0] = temps[0];
- th->cached_temp[1] = temps[1];
- th->cached_temp[2] = temps[2];
-#endif
+ display_stats(th);
+#endif
+
}
return 0;
}
-static void
-set_limit(struct thermostat *th, int i)
+static void set_limit(struct thermostat *th, int i)
{
/* Set CPU limit higher to avoid powerdowns */
th->limits[i] = default_limits_chip[i] + limit_adjust;
@@ -326,9 +360,9 @@
/* set our limits to normal */
th->limits[i] = default_limits_local[i] + limit_adjust;
}
-
-static int
-attach_one_thermostat(struct i2c_adapter *adapter, int addr, int busno)
+
+static int attach_one_thermostat(struct i2c_adapter *adapter, int addr,
+ int busno)
{
struct thermostat* th;
int rc;
@@ -336,9 +370,13 @@
if (thermostat)
return 0;
- th = (struct thermostat *)kmalloc(sizeof(struct thermostat), GFP_KERNEL);
+
+ th = (struct thermostat *)
+ kmalloc(sizeof(struct thermostat), GFP_KERNEL);
+
if (!th)
return -ENOMEM;
+
memset(th, 0, sizeof(*th));
th->clt.addr = addr;
th->clt.adapter = adapter;
@@ -348,15 +386,16 @@
rc = read_reg(th, 0);
if (rc < 0) {
- printk(KERN_ERR "adt746x: Thermostat failed to read config from bus %d !\n",
- busno);
+ printk(KERN_ERR "adt746x: Thermostat failed to read config "
+ "from bus %d !\n",
+ busno);
kfree(th);
return -ENODEV;
}
+
/* force manual control to start the fan quieter */
-
if (fan_speed == -1)
- fan_speed=128;
+ fan_speed = 64;
if(therm_type == ADT7460) {
printk(KERN_INFO "adt746x: ADT7460 initializing\n");
@@ -371,14 +410,16 @@
}
printk(KERN_INFO "adt746x: Lowering max temperatures from %d, %d, %d"
- " to %d, %d, %d\n",
- th->initial_limits[0], th->initial_limits[1], th->initial_limits[2],
- th->limits[0], th->limits[1], th->limits[2]);
+ " to %d, %d, %d\n",
+ th->initial_limits[0], th->initial_limits[1],
+ th->initial_limits[2], th->limits[0], th->limits[1],
+ th->limits[2]);
thermostat = th;
if (i2c_attach_client(&th->clt)) {
- printk(KERN_INFO "adt746x: Thermostat failed to attach client !\n");
+ printk(KERN_INFO "adt746x: Thermostat failed to attach "
+ "client !\n");
thermostat = NULL;
kfree(th);
return -ENODEV;
@@ -387,10 +428,14 @@
/* be sure to really write fan speed the first time */
th->last_speed[0] = -2;
th->last_speed[1] = -2;
-
+ th->last_var[0] = -80;
+ th->last_var[1] = -80;
+
if (fan_speed != -1) {
+ /* manual mode, stop fans */
write_both_fan_speed(th, 0);
} else {
+ /* automatic mode */
write_both_fan_speed(th, -1);
}
@@ -507,8 +552,9 @@
therm_bus = ((*prop) >> 8) & 0x0f;
therm_address = ((*prop) & 0xff) >> 1;
- printk(KERN_INFO "adt746x: Thermostat bus: %d, address: 0x%02x, limit_adjust: %d, fan_speed: %d\n",
- therm_bus, therm_address, limit_adjust, fan_speed);
+ printk(KERN_INFO "adt746x: Thermostat bus: %d, address: 0x%02x, "
+ "limit_adjust: %d, fan_speed: %d\n",
+ therm_bus, therm_address, limit_adjust, fan_speed);
of_dev = of_platform_device_create(np, "temperatures");
@@ -545,8 +591,11 @@
device_remove_file(&of_dev->dev, &dev_attr_limit_adjust);
device_remove_file(&of_dev->dev, &dev_attr_specified_fan_speed);
device_remove_file(&of_dev->dev, &dev_attr_cpu_fan_speed);
+
if(therm_type == ADT7460)
- device_remove_file(&of_dev->dev, &dev_attr_gpu_fan_speed);
+ device_remove_file(&of_dev->dev,
+ &dev_attr_gpu_fan_speed);
+
of_device_unregister(of_dev);
}
i2c_del_driver(&thermostat_driver);
diff -ur a/drivers/net/sungem.c b/drivers/net/sungem.c
--- a/drivers/net/sungem.c 2004-11-26 20:09:44.799524216 +0100
+++ b/drivers/net/sungem.c 2004-11-26 19:54:41.000000000 +0100
@@ -10,9 +10,24 @@
* (C) 2004 by Eric Lemoine (eric.lemoine@gmail.com)
*
* TODO:
- * - Get rid of all those nasty mdelay's and replace them
- * with schedule_timeout.
- * - Implement WOL
+ * - Now that the driver was significantly simplified, I need to rework
+ * the locking. I'm sure we don't need _2_ spinlocks, and we probably
+ * can avoid taking most of them for so long period of time (and schedule
+ * instead). The main issues at this point are caused by the netdev layer
+ * though:
+ *
+ * gem_change_mtu() and gem_set_multicast() are called with a read_lock()
+ * help by net/core/dev.c, thus they can't schedule. That means they can't
+ * call netif_poll_disable() neither, thus force gem_poll() to keep a spinlock
+ * where it could have been dropped. change_mtu especially would love also to
+ * be able to msleep instead of horrid locked delays when resetting the HW,
+ * but that read_lock() makes it impossible, unless I defer it's action to
+ * the reset task, which means it'll be asynchronous (won't take effect until
+ * the system schedules a bit).
+ *
+ * Also, it would probably be possible to also remove most of the long-life
+ * locking in open/resume code path (gem_reinit_chip) by beeing more careful
+ * about when we can start taking interrupts or get xmit() called...
*/
#include <linux/module.h>
@@ -196,6 +211,29 @@
writel(GREG_STAT_NAPI | GREG_STAT_TXDONE, gp->regs + GREG_IMASK);
}
+static void gem_get_cell(struct gem *gp)
+{
+ gp->cell_enabled++;
+#ifdef CONFIG_PPC_PMAC
+ if (gp->cell_enabled == 1) {
+ mb();
+ pmac_call_feature(PMAC_FTR_GMAC_ENABLE, gp->of_node, 0, 1);
+ udelay(10);
+ }
+#endif /* CONFIG_PPC_PMAC */
+}
+
+/* Turn off the chip's clock */
+static void gem_put_cell(struct gem *gp)
+{
+ gp->cell_enabled--;
+ BUG_ON(gp->cell_enabled < 0);
+#ifdef CONFIG_PPC_PMAC
+ if (gp->cell_enabled == 0)
+ pmac_call_feature(PMAC_FTR_GMAC_ENABLE, gp->of_node, 0, 0);
+#endif /* CONFIG_PPC_PMAC */
+}
+
static void gem_handle_mif_event(struct gem *gp, u32 reg_val, u32 changed_bits)
{
if (netif_msg_intr(gp))
@@ -597,7 +635,7 @@
return 0;
do_reset:
- gp->reset_task_pending = 2;
+ gp->reset_task_pending = 1;
schedule_work(&gp->reset_task);
return 1;
@@ -823,6 +861,9 @@
struct gem *gp = dev->priv;
unsigned long flags;
+ /*
+ * NAPI locking nightmare: See comment at head of driver
+ */
spin_lock_irqsave(&gp->lock, flags);
do {
@@ -874,8 +915,11 @@
struct gem *gp = dev->priv;
unsigned long flags;
- /* Swallow interrupts when shutting the chip down */
- if (!gp->hw_running)
+ /* Swallow interrupts when shutting the chip down, though
+ * that shouldn't happen, we should have done free_irq() at
+ * this point...
+ */
+ if (!gp->running)
return IRQ_HANDLED;
spin_lock_irqsave(&gp->lock, flags);
@@ -916,7 +960,7 @@
struct gem *gp = dev->priv;
printk(KERN_ERR "%s: transmit timed out, resetting\n", dev->name);
- if (!gp->hw_running) {
+ if (!gp->running) {
printk("%s: hrm.. hw not running !\n", dev->name);
return;
}
@@ -934,7 +978,7 @@
spin_lock_irq(&gp->lock);
spin_lock(&gp->tx_lock);
- gp->reset_task_pending = 2;
+ gp->reset_task_pending = 1;
schedule_work(&gp->reset_task);
spin_unlock(&gp->tx_lock);
@@ -975,6 +1019,11 @@
local_irq_restore(flags);
return NETDEV_TX_LOCKED;
}
+ /* We raced with gem_do_stop() */
+ if (!gp->running) {
+ spin_unlock_irqrestore(&gp->tx_lock, flags);
+ return NETDEV_TX_BUSY;
+ }
/* This is a hard error, log it. */
if (TX_BUFFS_AVAIL(gp) <= (skb_shinfo(skb)->nr_frags + 1)) {
@@ -1073,46 +1122,10 @@
return NETDEV_TX_OK;
}
-/* Jumbo-grams don't seem to work :-( */
-#define GEM_MIN_MTU 68
-#if 1
-#define GEM_MAX_MTU 1500
-#else
-#define GEM_MAX_MTU 9000
-#endif
-
-static int gem_change_mtu(struct net_device *dev, int new_mtu)
-{
- struct gem *gp = dev->priv;
-
- if (new_mtu < GEM_MIN_MTU || new_mtu > GEM_MAX_MTU)
- return -EINVAL;
-
- if (!netif_running(dev) || !netif_device_present(dev)) {
- /* We'll just catch it later when the
- * device is up'd or resumed.
- */
- dev->mtu = new_mtu;
- return 0;
- }
-
- spin_lock_irq(&gp->lock);
- spin_lock(&gp->tx_lock);
- dev->mtu = new_mtu;
- gp->reset_task_pending = 1;
- schedule_work(&gp->reset_task);
- spin_unlock(&gp->tx_lock);
- spin_unlock_irq(&gp->lock);
-
- flush_scheduled_work();
-
- return 0;
-}
-
#define STOP_TRIES 32
/* Must be invoked under gp->lock and gp->tx_lock. */
-static void gem_stop(struct gem *gp)
+static void gem_reset(struct gem *gp)
{
int limit;
u32 val;
@@ -1140,7 +1153,7 @@
/* Must be invoked under gp->lock and gp->tx_lock. */
static void gem_start_dma(struct gem *gp)
{
- unsigned long val;
+ u32 val;
/* We are ready to rock, turn everything on. */
val = readl(gp->regs + TXDMA_CFG);
@@ -1155,10 +1168,31 @@
(void) readl(gp->regs + MAC_RXCFG);
udelay(100);
- writel(GREG_STAT_TXDONE, gp->regs + GREG_IMASK);
+ gem_enable_ints(gp);
writel(RX_RING_SIZE - 4, gp->regs + RXDMA_KICK);
+}
+
+/* Must be invoked under gp->lock and gp->tx_lock. DMA won't be
+ * actually stopped before about 4ms tho ...
+ */
+static void gem_stop_dma(struct gem *gp)
+{
+ u32 val;
+
+ /* We are done rocking, turn everything off. */
+ val = readl(gp->regs + TXDMA_CFG);
+ writel(val & ~TXDMA_CFG_ENABLE, gp->regs + TXDMA_CFG);
+ val = readl(gp->regs + RXDMA_CFG);
+ writel(val & ~RXDMA_CFG_ENABLE, gp->regs + RXDMA_CFG);
+ val = readl(gp->regs + MAC_TXCFG);
+ writel(val & ~MAC_TXCFG_ENAB, gp->regs + MAC_TXCFG);
+ val = readl(gp->regs + MAC_RXCFG);
+ writel(val & ~MAC_RXCFG_ENAB, gp->regs + MAC_RXCFG);
+
+ (void) readl(gp->regs + MAC_RXCFG);
+ /* Need to wait a bit ... done by the caller */
}
@@ -1219,10 +1253,10 @@
if (speed == 0)
speed = SPEED_10;
- /* If HW is down, we don't try to actually setup the PHY, we
+ /* If we are asleep, we don't try to actually setup the PHY, we
* just store the settings
*/
- if (!gp->hw_running) {
+ if (gp->asleep) {
gp->phy_mii.autoneg = gp->want_autoneg = autoneg;
gp->phy_mii.speed = speed;
gp->phy_mii.duplex = duplex;
@@ -1279,6 +1313,9 @@
printk(KERN_INFO "%s: Link is up at %d Mbps, %s-duplex.\n",
gp->dev->name, speed, (full_duplex ? "full" : "half"));
+ if (!gp->running)
+ return 0;
+
val = (MAC_TXCFG_EIPG0 | MAC_TXCFG_NGU);
if (full_duplex) {
val |= (MAC_TXCFG_ICS | MAC_TXCFG_ICOLL);
@@ -1405,48 +1442,19 @@
}
}
-static void gem_init_rings(struct gem *);
-static void gem_init_hw(struct gem *, int);
-
-static void gem_reset_task(void *data)
-{
- struct gem *gp = (struct gem *) data;
-
- netif_poll_disable(gp->dev);
- spin_lock_irq(&gp->lock);
- spin_lock(&gp->tx_lock);
-
- if (gp->hw_running && gp->opened) {
- netif_stop_queue(gp->dev);
-
- /* Reset the chip & rings */
- gem_stop(gp);
- gem_init_rings(gp);
-
- gem_init_hw(gp,
- (gp->reset_task_pending == 2));
-
- netif_wake_queue(gp->dev);
- }
- gp->reset_task_pending = 0;
-
- spin_unlock(&gp->tx_lock);
- spin_unlock_irq(&gp->lock);
- netif_poll_enable(gp->dev);
-}
-
static void gem_link_timer(unsigned long data)
{
struct gem *gp = (struct gem *) data;
int restart_aneg = 0;
- if (!gp->hw_running)
+ if (gp->asleep)
return;
spin_lock_irq(&gp->lock);
spin_lock(&gp->tx_lock);
+ gem_get_cell(gp);
- /* If the link of task is still pending, we just
+ /* If the reset task is still pending, we just
* reschedule the link timer
*/
if (gp->reset_task_pending)
@@ -1462,8 +1470,7 @@
if ((val & PCS_MIISTAT_LS) != 0) {
gp->lstate = link_up;
netif_carrier_on(gp->dev);
- if (gp->opened)
- (void)gem_set_link_modes(gp);
+ (void)gem_set_link_modes(gp);
}
goto restart;
}
@@ -1484,7 +1491,7 @@
} else if (gp->lstate != link_up) {
gp->lstate = link_up;
netif_carrier_on(gp->dev);
- if (gp->opened && gem_set_link_modes(gp))
+ if (gem_set_link_modes(gp))
restart_aneg = 1;
}
} else {
@@ -1497,7 +1504,7 @@
printk(KERN_INFO "%s: Link down\n",
gp->dev->name);
netif_carrier_off(gp->dev);
- gp->reset_task_pending = 2;
+ gp->reset_task_pending = 1;
schedule_work(&gp->reset_task);
restart_aneg = 1;
} else if (++gp->timer_ticks > 10) {
@@ -1514,6 +1521,7 @@
restart:
mod_timer(&gp->link_timer, jiffies + ((12 * HZ) / 10));
out_unlock:
+ gem_put_cell(gp);
spin_unlock(&gp->tx_lock);
spin_unlock_irq(&gp->lock);
}
@@ -1619,59 +1627,40 @@
wmb();
}
-/* Must be invoked under gp->lock and gp->tx_lock. */
+/* Init PHY interface and start link poll state machine */
static void gem_init_phy(struct gem *gp)
{
u32 mifcfg;
-
+
/* Revert MIF CFG setting done on stop_phy */
mifcfg = readl(gp->regs + MIF_CFG);
mifcfg &= ~MIF_CFG_BBMODE;
writel(mifcfg, gp->regs + MIF_CFG);
-#ifdef CONFIG_PPC_PMAC
if (gp->pdev->vendor == PCI_VENDOR_ID_APPLE) {
- int i, j;
+ int i;
/* Those delay sucks, the HW seem to love them though, I'll
* serisouly consider breaking some locks here to be able
* to schedule instead
*/
- pmac_call_feature(PMAC_FTR_GMAC_PHY_RESET, gp->of_node, 0, 0);
- for (j = 0; j < 3; j++) {
+ for (i = 0; i < 3; i++) {
+#ifdef CONFIG_PPC_PMAC
+ pmac_call_feature(PMAC_FTR_GMAC_PHY_RESET, gp->of_node, 0, 0);
+ msleep(20);
+#endif
/* Some PHYs used by apple have problem getting back to us,
- * we _know_ it's actually at addr 0 or 1, that's a hack, but
- * it helps to do that reset now. I suspect some motherboards
- * don't wire the PHY reset line properly, thus the PHY doesn't
- * come back with the above pmac_call_feature.
+ * we do an additional reset here
*/
- gp->mii_phy_addr = 0;
- phy_write(gp, MII_BMCR, BMCR_RESET);
- gp->mii_phy_addr = 1;
phy_write(gp, MII_BMCR, BMCR_RESET);
- /* We should probably break some locks here and schedule... */
- mdelay(10);
-
- /* On K2, we only probe the internal PHY at address 1, other
- * addresses tend to return garbage.
- */
- if (gp->pdev->device == PCI_DEVICE_ID_APPLE_K2_GMAC)
+ msleep(20);
+ if (phy_read(gp, MII_BMCR) != 0xffff)
break;
-
- for (i = 0; i < 32; i++) {
- gp->mii_phy_addr = i;
- if (phy_read(gp, MII_BMCR) != 0xffff)
- break;
- }
- if (i == 32) {
+ if (i == 2)
printk(KERN_WARNING "%s: GMAC PHY not responding !\n",
gp->dev->name);
- gp->mii_phy_addr = 0;
- } else
- break;
}
}
-#endif /* CONFIG_PPC_PMAC */
if (gp->pdev->vendor == PCI_VENDOR_ID_SUN &&
gp->pdev->device == PCI_DEVICE_ID_SUN_GEM) {
@@ -1755,6 +1744,16 @@
val |= PCS_SCTRL_LOOP;
writel(val, gp->regs + PCS_SCTRL);
}
+
+ /* Default aneg parameters */
+ gp->timer_ticks = 0;
+ gp->lstate = link_down;
+ netif_carrier_off(gp->dev);
+
+ /* Can I advertise gigabit here ? I'd need BCM PHY docs... */
+ spin_lock_irq(&gp->lock);
+ gem_begin_auto_negotiation(gp, NULL);
+ spin_unlock_irq(&gp->lock);
}
/* Must be invoked under gp->lock and gp->tx_lock. */
@@ -1796,8 +1795,7 @@
}
/* Must be invoked under gp->lock and gp->tx_lock. */
-static u32
-gem_setup_multicast(struct gem *gp)
+static u32 gem_setup_multicast(struct gem *gp)
{
u32 rxcfg = 0;
int i;
@@ -1914,6 +1912,10 @@
* make no use of those events other than to record them.
*/
writel(0xffffffff, gp->regs + MAC_MCMASK);
+
+ /* Don't enable GEM's WOL in normal operations
+ */
+ writel(0, gp->regs + WOL_WAKECSR);
}
/* Must be invoked under gp->lock and gp->tx_lock. */
@@ -1975,6 +1977,23 @@
gp->tx_fifo_sz = readl(gp->regs + TXDMA_FSZ) * 64;
gp->rx_fifo_sz = readl(gp->regs + RXDMA_FSZ) * 64;
gp->swrst_base = 0;
+
+ mif_cfg = readl(gp->regs + MIF_CFG);
+ mif_cfg &= ~(MIF_CFG_PSELECT|MIF_CFG_POLL|MIF_CFG_BBMODE|MIF_CFG_MDI1);
+ mif_cfg |= MIF_CFG_MDI0;
+ writel(mif_cfg, gp->regs + MIF_CFG);
+ writel(PCS_DMODE_MGM, gp->regs + PCS_DMODE);
+ writel(MAC_XIFCFG_OE, gp->regs + MAC_XIFCFG);
+
+ /* We hard-code the PHY address so we can properly bring it out of
+ * reset later on, we can't really probe it at this point, though
+ * that isn't an issue.
+ */
+ if (gp->pdev->device == PCI_DEVICE_ID_APPLE_K2_GMAC)
+ gp->mii_phy_addr = 1;
+ else
+ gp->mii_phy_addr = 0;
+
return 0;
}
@@ -2053,71 +2072,30 @@
}
/* Must be invoked under gp->lock and gp->tx_lock. */
-static void gem_init_hw(struct gem *gp, int restart_link)
+static void gem_reinit_chip(struct gem *gp)
{
- /* On Apple's gmac, I initialize the PHY only after
- * setting up the chip. It appears the gigabit PHYs
- * don't quite like beeing talked to on the GII when
- * the chip is not running, I suspect it might not
- * be clocked at that point. --BenH
- */
- if (restart_link)
- gem_init_phy(gp);
- gem_init_pause_thresholds(gp);
- gem_init_dma(gp);
- gem_init_mac(gp);
-
- if (restart_link) {
- /* Default aneg parameters */
- gp->timer_ticks = 0;
- gp->lstate = link_down;
- netif_carrier_off(gp->dev);
+ /* Reset the chip */
+ gem_reset(gp);
- /* Can I advertise gigabit here ? I'd need BCM PHY docs... */
- gem_begin_auto_negotiation(gp, NULL);
- } else {
- if (gp->lstate == link_up) {
- netif_carrier_on(gp->dev);
- gem_set_link_modes(gp);
- }
- }
-}
+ /* Make sure ints are disabled */
+ gem_disable_ints(gp);
-#ifdef CONFIG_PPC_PMAC
-/* Enable the chip's clock and make sure it's config space is
- * setup properly. There appear to be no need to restore the
- * base addresses.
- */
-static void gem_apple_powerup(struct gem *gp)
-{
- u32 mif_cfg;
+ /* Allocate & setup ring buffers */
+ gem_init_rings(gp);
- mb();
- pmac_call_feature(PMAC_FTR_GMAC_ENABLE, gp->of_node, 0, 1);
+ /* Configure pause thresholds */
+ gem_init_pause_thresholds(gp);
- udelay(10);
- pci_enable_device(gp->pdev);
- pci_set_master(gp->pdev);
+ /* Configure pause thresholds */
+ gem_init_pause_thresholds(gp);
- mif_cfg = readl(gp->regs + MIF_CFG);
- mif_cfg &= ~(MIF_CFG_PSELECT|MIF_CFG_POLL|MIF_CFG_BBMODE|MIF_CFG_MDI1);
- mif_cfg |= MIF_CFG_MDI0;
- writel(mif_cfg, gp->regs + MIF_CFG);
- writel(PCS_DMODE_MGM, gp->regs + PCS_DMODE);
- writel(MAC_XIFCFG_OE, gp->regs + MAC_XIFCFG);
-}
-
-/* Turn off the chip's clock */
-static void gem_apple_powerdown(struct gem *gp)
-{
- pci_disable_device(gp->pdev);
- pmac_call_feature(PMAC_FTR_GMAC_ENABLE, gp->of_node, 0, 0);
+ /* Init DMA & MAC engines */
+ gem_init_dma(gp);
+ gem_init_mac(gp);
}
-#endif /* CONFIG_PPC_PMAC */
-
/* Must be invoked with no lock held. */
-static void gem_stop_phy(struct gem *gp)
+static void gem_stop_phy(struct gem *gp, int wol)
{
u32 mifcfg;
unsigned long flags;
@@ -2134,8 +2112,22 @@
mifcfg &= ~MIF_CFG_POLL;
writel(mifcfg, gp->regs + MIF_CFG);
- if (gp->wake_on_lan) {
- /* Setup wake-on-lan */
+ if (wol) {
+ unsigned char *e = &gp->dev->dev_addr[0];
+ u32 csr;
+
+ /* Setup wake-on-lan for MAGIC packet */
+ writel(MAC_RXCFG_HFE | MAC_RXCFG_SFCS | MAC_RXCFG_ENAB,
+ gp->regs + MAC_RXCFG);
+ writel((e[4] << 8) | e[5], gp->regs + WOL_MATCH0);
+ writel((e[2] << 8) | e[3], gp->regs + WOL_MATCH1);
+ writel((e[0] << 8) | e[1], gp->regs + WOL_MATCH2);
+
+ writel(WOL_MCOUNT_N | WOL_MCOUNT_M, gp->regs + WOL_MCOUNT);
+ csr = WOL_WAKECSR_ENABLE;
+ if ((readl(gp->regs + MAC_XIFCFG) & MAC_XIFCFG_GMII) == 0)
+ csr |= WOL_WAKECSR_MII;
+ writel(csr, gp->regs + WOL_WAKECSR);
} else {
writel(0, gp->regs + MAC_RXCFG);
(void)readl(gp->regs + MAC_RXCFG);
@@ -2151,20 +2143,21 @@
writel(0, gp->regs + TXDMA_CFG);
writel(0, gp->regs + RXDMA_CFG);
- if (!gp->wake_on_lan) {
+ /* No need to take the lock here */
+ if (!wol) {
spin_lock_irqsave(&gp->lock, flags);
spin_lock(&gp->tx_lock);
- gem_stop(gp);
+ gem_reset(gp);
writel(MAC_TXRST_CMD, gp->regs + MAC_TXRST);
writel(MAC_RXRST_CMD, gp->regs + MAC_RXRST);
spin_unlock(&gp->tx_lock);
spin_unlock_irqrestore(&gp->lock, flags);
}
- if (found_mii_phy(gp) && gp->phy_mii.def->ops->suspend)
- gp->phy_mii.def->ops->suspend(&gp->phy_mii, 0 /* wake on lan options */);
+ if (!wol && found_mii_phy(gp) && gp->phy_mii.def->ops->suspend)
+ gp->phy_mii.def->ops->suspend(&gp->phy_mii);
- if (!gp->wake_on_lan) {
+ if (!wol) {
/* According to Apple, we must set the MDIO pins to this begnign
* state or we may 1) eat more current, 2) damage some PHYs
*/
@@ -2177,181 +2170,160 @@
}
}
-/* Shut down the chip, must be called with pm_sem held. */
-static void gem_shutdown(struct gem *gp)
+
+static int gem_do_start(struct net_device *dev)
{
- /* Make us not-running to avoid timers respawning
- * and swallow irqs
- */
- gp->hw_running = 0;
- wmb();
+ struct gem *gp = dev->priv;
+ unsigned long flags;
- /* Stop the link timer */
- del_timer_sync(&gp->link_timer);
+ spin_lock_irqsave(&gp->lock, flags);
+ spin_lock(&gp->tx_lock);
- /* Stop the reset task */
- while (gp->reset_task_pending)
- yield();
-
- /* Actually stop the chip */
- if (gp->pdev->vendor == PCI_VENDOR_ID_APPLE) {
- gem_stop_phy(gp);
+ /* Enable the cell */
+ gem_get_cell(gp);
-#ifdef CONFIG_PPC_PMAC
- /* Power down the chip */
- gem_apple_powerdown(gp);
-#endif /* CONFIG_PPC_PMAC */
- } else{
- unsigned long flags;
+ /* Init & setup chip hardware */
+ gem_reinit_chip(gp);
+
+ gp->running = 1;
+
+ if (gp->lstate == link_up) {
+ netif_carrier_on(gp->dev);
+ gem_set_link_modes(gp);
+ }
+
+ netif_wake_queue(gp->dev);
+
+ spin_unlock(&gp->tx_lock);
+ spin_unlock_irqrestore(&gp->lock, flags);
+
+ if (request_irq(gp->pdev->irq, gem_interrupt,
+ SA_SHIRQ, dev->name, (void *)dev)) {
+ printk(KERN_ERR "%s: failed to request irq !\n", gp->dev->name);
spin_lock_irqsave(&gp->lock, flags);
spin_lock(&gp->tx_lock);
- gem_stop(gp);
+
+ gp->running = 0;
+ gem_reset(gp);
+ gem_clean_rings(gp);
+ gem_put_cell(gp);
+
spin_unlock(&gp->tx_lock);
spin_unlock_irqrestore(&gp->lock, flags);
+
+ return -EAGAIN;
}
+
+ return 0;
}
-static void gem_pm_task(void *data)
+static void gem_do_stop(struct net_device *dev, int wol)
{
- struct gem *gp = (struct gem *) data;
+ struct gem *gp = dev->priv;
+ unsigned long flags;
- /* We assume if we can't lock the pm_sem, then open() was
- * called again (or suspend()), and we can safely ignore
- * the PM request
- */
- if (down_trylock(&gp->pm_sem))
- return;
+ spin_lock_irqsave(&gp->lock, flags);
+ spin_lock(&gp->tx_lock);
- /* Driver was re-opened or already shut down */
- if (gp->opened || !gp->hw_running) {
- up(&gp->pm_sem);
- return;
- }
+ gp->running = 0;
- gem_shutdown(gp);
+ /* Stop netif queue */
+ netif_stop_queue(dev);
- up(&gp->pm_sem);
-}
+ /* Make sure ints are disabled */
+ gem_disable_ints(gp);
-static void gem_pm_timer(unsigned long data)
-{
- struct gem *gp = (struct gem *) data;
+ /* We can drop the lock now */
+ spin_unlock(&gp->tx_lock);
+ spin_unlock_irqrestore(&gp->lock, flags);
+
+ /* If we are going to sleep with WOL */
+ if (wol)
+ gem_stop_dma(gp);
+ else
+ gem_reset(gp);
+ msleep(10);
+
+ /* Get rid of rings */
+ gem_clean_rings(gp);
+
+ /* No irq needed anymore */
+ free_irq(gp->pdev->irq, (void *) dev);
- schedule_work(&gp->pm_task);
+ /* Cell not needed neither if no WOL */
+ if (!wol) {
+ spin_lock_irqsave(&gp->lock, flags);
+ gem_put_cell(gp);
+ spin_unlock_irqrestore(&gp->lock, flags);
+ }
}
-static int gem_open(struct net_device *dev)
+static void gem_reset_task(void *data)
{
- struct gem *gp = dev->priv;
- int hw_was_up;
+ struct gem *gp = (struct gem *) data;
down(&gp->pm_sem);
- hw_was_up = gp->hw_running;
+ netif_poll_disable(gp->dev);
- /* Stop the PM timer/task */
- del_timer(&gp->pm_timer);
- flush_scheduled_work();
+ spin_lock_irq(&gp->lock);
+ spin_lock(&gp->tx_lock);
- /* The power-management semaphore protects the hw_running
- * etc. state so it is safe to do this bit without gp->lock
- */
- if (!gp->hw_running) {
-#ifdef CONFIG_PPC_PMAC
- /* First, we need to bring up the chip */
- if (gp->pdev->vendor == PCI_VENDOR_ID_APPLE) {
- gem_apple_powerup(gp);
- gem_check_invariants(gp);
- }
-#endif /* CONFIG_PPC_PMAC */
+ if (gp->running == 0)
+ goto not_running;
- /* Reset the chip */
- spin_lock_irq(&gp->lock);
- spin_lock(&gp->tx_lock);
- gem_stop(gp);
- spin_unlock(&gp->tx_lock);
- spin_unlock_irq(&gp->lock);
+ if (gp->running) {
+ netif_stop_queue(gp->dev);
- gp->hw_running = 1;
+ /* Reset the chip & rings */
+ gem_reinit_chip(gp);
+ if (gp->lstate == link_up)
+ gem_set_link_modes(gp);
+ netif_wake_queue(gp->dev);
}
+ not_running:
+ gp->reset_task_pending = 0;
- /* We can now request the interrupt as we know it's masked
- * on the controller
- */
- if (request_irq(gp->pdev->irq, gem_interrupt,
- SA_SHIRQ, dev->name, (void *)dev)) {
- printk(KERN_ERR "%s: failed to request irq !\n", gp->dev->name);
+ spin_unlock(&gp->tx_lock);
+ spin_unlock_irq(&gp->lock);
- spin_lock_irq(&gp->lock);
- spin_lock(&gp->tx_lock);
-#ifdef CONFIG_PPC_PMAC
- if (!hw_was_up && gp->pdev->vendor == PCI_VENDOR_ID_APPLE)
- gem_apple_powerdown(gp);
-#endif /* CONFIG_PPC_PMAC */
- /* Fire the PM timer that will shut us down in about 10 seconds */
- gp->pm_timer.expires = jiffies + 10*HZ;
- add_timer(&gp->pm_timer);
- up(&gp->pm_sem);
- spin_unlock(&gp->tx_lock);
- spin_unlock_irq(&gp->lock);
-
- return -EAGAIN;
- }
+ netif_poll_enable(gp->dev);
- spin_lock_irq(&gp->lock);
- spin_lock(&gp->tx_lock);
+ up(&gp->pm_sem);
+}
- /* Allocate & setup ring buffers */
- gem_init_rings(gp);
- /* Init & setup chip hardware */
- gem_init_hw(gp, !hw_was_up);
+static int gem_open(struct net_device *dev)
+{
+ struct gem *gp = dev->priv;
+ int rc = 0;
- gp->opened = 1;
+ down(&gp->pm_sem);
- spin_unlock(&gp->tx_lock);
- spin_unlock_irq(&gp->lock);
+ /* We need the cell enabled */
+ if (!gp->asleep)
+ rc = gem_do_start(dev);
+ gp->opened = (rc == 0);
up(&gp->pm_sem);
- return 0;
+ return rc;
}
static int gem_close(struct net_device *dev)
{
struct gem *gp = dev->priv;
- /* Make sure we don't get distracted by suspend/resume */
- down(&gp->pm_sem);
-
/* Note: we don't need to call netif_poll_disable() here because
* our caller (dev_close) already did it for us
*/
- /* Stop traffic, mark us closed */
- spin_lock_irq(&gp->lock);
- spin_lock(&gp->tx_lock);
+ down(&gp->pm_sem);
gp->opened = 0;
-
- netif_stop_queue(dev);
-
- /* Stop chip */
- gem_stop(gp);
-
- /* Get rid of rings */
- gem_clean_rings(gp);
-
- /* Bye, the pm timer will finish the job */
- free_irq(gp->pdev->irq, (void *) dev);
-
- spin_unlock(&gp->tx_lock);
- spin_unlock_irq(&gp->lock);
-
- /* Fire the PM timer that will shut us down in about 10 seconds */
- gp->pm_timer.expires = jiffies + 10*HZ;
- add_timer(&gp->pm_timer);
+ if (!gp->asleep)
+ gem_do_stop(dev, 0);
up(&gp->pm_sem);
@@ -2363,45 +2335,63 @@
{
struct net_device *dev = pci_get_drvdata(pdev);
struct gem *gp = dev->priv;
+ unsigned long flags;
- netif_poll_disable(dev);
-
- /* We hold the PM semaphore during entire driver
- * sleep time
- */
down(&gp->pm_sem);
+ netif_poll_disable(dev);
+
printk(KERN_INFO "%s: suspending, WakeOnLan %s\n",
- dev->name, gp->wake_on_lan ? "enabled" : "disabled");
+ dev->name,
+ (gp->wake_on_lan && gp->opened) ? "enabled" : "disabled");
- /* If the driver is opened, we stop the DMA */
- if (gp->opened) {
- spin_lock_irq(&gp->lock);
- spin_lock(&gp->tx_lock);
+ /* Keep the cell enabled during the entire operation */
+ spin_lock_irqsave(&gp->lock, flags);
+ spin_lock(&gp->tx_lock);
+ gem_get_cell(gp);
+ spin_unlock(&gp->tx_lock);
+ spin_unlock_irqrestore(&gp->lock, flags);
+ /* If the driver is opened, we stop the MAC */
+ if (gp->opened) {
/* Stop traffic, mark us closed */
netif_device_detach(dev);
- /* Stop chip */
- gem_stop(gp);
+ /* Switch off MAC, remember WOL setting */
+ gp->asleep_wol = gp->wake_on_lan;
+ gem_do_stop(dev, gp->asleep_wol);
+ msleep(10);
+ } else
+ gp->asleep_wol = 0;
- /* Get rid of ring buffers */
- gem_clean_rings(gp);
+ /* Mark us asleep */
+ gp->asleep = 1;
+ wmb();
- spin_unlock(&gp->tx_lock);
- spin_unlock_irq(&gp->lock);
+ /* Stop the link timer */
+ del_timer_sync(&gp->link_timer);
- if (gp->pdev->vendor == PCI_VENDOR_ID_APPLE)
- disable_irq(gp->pdev->irq);
- }
+ /* Now we release the semaphore to not block the reset task who
+ * can take it too. We are marked asleep, so there will be no
+ * conflict here
+ */
+ up(&gp->pm_sem);
- if (gp->hw_running) {
- /* Kill PM timer if any */
- del_timer_sync(&gp->pm_timer);
- flush_scheduled_work();
+ /* Wait for a pending reset task to complete */
+ while (gp->reset_task_pending)
+ yield();
+ flush_scheduled_work();
- gem_shutdown(gp);
- }
+ /* Shut the PHY down eventually and setup WOL */
+ gem_stop_phy(gp, gp->asleep_wol);
+
+ /* Make sure bus master is disabled */
+ pci_disable_device(gp->pdev);
+
+ /* Release the cell, no need to take a lock at this point since
+ * nothing else can happen now
+ */
+ gem_put_cell(gp);
return 0;
}
@@ -2410,36 +2400,74 @@
{
struct net_device *dev = pci_get_drvdata(pdev);
struct gem *gp = dev->priv;
+ unsigned long flags;
printk(KERN_INFO "%s: resuming\n", dev->name);
- if (gp->opened) {
-#ifdef CONFIG_PPC_PMAC
- /* First, we need to bring up the chip */
- if (gp->pdev->vendor == PCI_VENDOR_ID_APPLE) {
- gem_apple_powerup(gp);
- gem_check_invariants(gp);
- }
-#endif /* CONFIG_PPC_PMAC */
- spin_lock_irq(&gp->lock);
- spin_lock(&gp->tx_lock);
+ down(&gp->pm_sem);
- gem_stop(gp);
- gp->hw_running = 1;
- gem_init_rings(gp);
- gem_init_hw(gp, 1);
+ /* Keep the cell enabled during the entire operation, no need to
+ * take a lock here tho since nothing else can happen while we are
+ * marked asleep
+ */
+ gem_get_cell(gp);
+
+ /* Make sure PCI access and bus master are enabled */
+ if (pci_enable_device(gp->pdev)) {
+ printk(KERN_ERR "%s: Can't re-enable chip !\n",
+ dev->name);
+ /* Put cell and forget it for now, it will be considered as
+ * still asleep, a new sleep cycle may bring it back
+ */
+ gem_put_cell(gp);
+ up(&gp->pm_sem);
+ return 0;
+ }
+ pci_set_master(gp->pdev);
- spin_unlock(&gp->tx_lock);
- spin_unlock_irq(&gp->lock);
+ /* Reset everything */
+ gem_reset(gp);
+ /* Mark us woken up */
+ gp->asleep = 0;
+ wmb();
+
+ /* Bring the PHY back. Again, lock is useless at this point as
+ * nothing can be happening until we restart the whole thing
+ */
+ gem_init_phy(gp);
+
+ /* If we were opened, bring everything back */
+ if (gp->opened) {
+ /* Restart MAC */
+ gem_do_start(dev);
+
+ /* Re-attach net device */
netif_device_attach(dev);
- if (gp->pdev->vendor == PCI_VENDOR_ID_APPLE)
- enable_irq(gp->pdev->irq);
+
}
- up(&gp->pm_sem);
+
+ spin_lock_irqsave(&gp->lock, flags);
+ spin_lock(&gp->tx_lock);
+
+ /* If we had WOL enabled, the cell clock was never turned off during
+ * sleep, so we end up beeing unbalanced. Fix that here
+ */
+ if (gp->asleep_wol)
+ gem_put_cell(gp);
+
+ /* This function doesn't need to hold the cell, it will be held if the
+ * driver is open by gem_do_start().
+ */
+ gem_put_cell(gp);
+
+ spin_unlock(&gp->tx_lock);
+ spin_unlock_irqrestore(&gp->lock, flags);
netif_poll_enable(dev);
+ up(&gp->pm_sem);
+
return 0;
}
#endif /* CONFIG_PM */
@@ -2452,7 +2480,10 @@
spin_lock_irq(&gp->lock);
spin_lock(&gp->tx_lock);
- if (gp->hw_running) {
+ /* I have seen this being called while the PM was in progress,
+ * so we shield against this
+ */
+ if (gp->running) {
stats->rx_crc_errors += readl(gp->regs + MAC_FCSERR);
writel(0, gp->regs + MAC_FCSERR);
@@ -2482,12 +2513,12 @@
u32 rxcfg, rxcfg_new;
int limit = 10000;
- if (!gp->hw_running)
- return;
-
spin_lock_irq(&gp->lock);
spin_lock(&gp->tx_lock);
+ if (!gp->running)
+ goto bail;
+
netif_stop_queue(dev);
rxcfg = readl(gp->regs + MAC_RXCFG);
@@ -2510,9 +2541,46 @@
writel(rxcfg, gp->regs + MAC_RXCFG);
netif_wake_queue(dev);
+ bail:
+ spin_unlock(&gp->tx_lock);
+ spin_unlock_irq(&gp->lock);
+}
+
+/* Jumbo-grams don't seem to work :-( */
+#define GEM_MIN_MTU 68
+#if 1
+#define GEM_MAX_MTU 1500
+#else
+#define GEM_MAX_MTU 9000
+#endif
+
+static int gem_change_mtu(struct net_device *dev, int new_mtu)
+{
+ struct gem *gp = dev->priv;
+ if (new_mtu < GEM_MIN_MTU || new_mtu > GEM_MAX_MTU)
+ return -EINVAL;
+
+ if (!netif_running(dev) || !netif_device_present(dev)) {
+ /* We'll just catch it later when the
+ * device is up'd or resumed.
+ */
+ dev->mtu = new_mtu;
+ return 0;
+ }
+
+ spin_lock_irq(&gp->lock);
+ spin_lock(&gp->tx_lock);
+ dev->mtu = new_mtu;
+ if (gp->running) {
+ gem_reinit_chip(gp);
+ if (gp->lstate == link_up)
+ gem_set_link_modes(gp);
+ }
spin_unlock(&gp->tx_lock);
spin_unlock_irq(&gp->lock);
+
+ return 0;
}
static void gem_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info)
@@ -2543,7 +2611,6 @@
/* Return current PHY settings */
spin_lock_irq(&gp->lock);
- spin_lock(&gp->tx_lock);
cmd->autoneg = gp->want_autoneg;
cmd->speed = gp->phy_mii.speed;
cmd->duplex = gp->phy_mii.duplex;
@@ -2555,7 +2622,6 @@
*/
if (cmd->advertising == 0)
cmd->advertising = cmd->supported;
- spin_unlock(&gp->tx_lock);
spin_unlock_irq(&gp->lock);
} else { // XXX PCS ?
cmd->supported =
@@ -2595,9 +2661,9 @@
/* Apply settings and restart link process. */
spin_lock_irq(&gp->lock);
- spin_lock(&gp->tx_lock);
+ gem_get_cell(gp);
gem_begin_auto_negotiation(gp, cmd);
- spin_unlock(&gp->tx_lock);
+ gem_put_cell(gp);
spin_unlock_irq(&gp->lock);
return 0;
@@ -2612,9 +2678,9 @@
/* Restart link process. */
spin_lock_irq(&gp->lock);
- spin_lock(&gp->tx_lock);
+ gem_get_cell(gp);
gem_begin_auto_negotiation(gp, NULL);
- spin_unlock(&gp->tx_lock);
+ gem_put_cell(gp);
spin_unlock_irq(&gp->lock);
return 0;
@@ -2631,7 +2697,31 @@
struct gem *gp = dev->priv;
gp->msg_enable = value;
}
-
+
+
+/* Add more when I understand how to program the chip */
+/* like WAKE_UCAST | WAKE_MCAST | WAKE_BCAST */
+
+#define WOL_SUPPORTED_MASK (WAKE_MAGIC)
+
+static void gem_get_wol(struct net_device *dev, struct ethtool_wolinfo *wol)
+{
+ struct gem *gp = dev->priv;
+
+ /* Add more when I understand how to program the chip */
+ wol->supported = WOL_SUPPORTED_MASK;
+ wol->wolopts = gp->wake_on_lan;
+}
+
+static int gem_set_wol(struct net_device *dev, struct ethtool_wolinfo *wol)
+{
+ struct gem *gp = dev->priv;
+
+ gp->wake_on_lan = wol->wolopts & WOL_SUPPORTED_MASK;
+
+ return 0;
+}
+
static struct ethtool_ops gem_ethtool_ops = {
.get_drvinfo = gem_get_drvinfo,
.get_link = ethtool_op_get_link,
@@ -2640,6 +2730,8 @@
.nway_reset = gem_nway_reset,
.get_msglevel = gem_get_msglevel,
.set_msglevel = gem_set_msglevel,
+ .get_wol = gem_get_wol,
+ .set_wol = gem_set_wol,
};
static int gem_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
@@ -2647,22 +2739,28 @@
struct gem *gp = dev->priv;
struct mii_ioctl_data *data = if_mii(ifr);
int rc = -EOPNOTSUPP;
-
+ unsigned long flags;
+
/* Hold the PM semaphore while doing ioctl's or we may collide
- * with open/close and power management and oops.
+ * with power management.
*/
down(&gp->pm_sem);
-
+
+ spin_lock_irqsave(&gp->lock, flags);
+ gem_get_cell(gp);
+ spin_unlock_irqrestore(&gp->lock, flags);
+
switch (cmd) {
case SIOCGMIIPHY: /* Get address of MII PHY in use. */
data->phy_id = gp->mii_phy_addr;
/* Fallthrough... */
case SIOCGMIIREG: /* Read MII PHY register. */
- if (!gp->hw_running)
- rc = -EIO;
+ if (!gp->running)
+ rc = -EAGAIN;
else {
- data->val_out = __phy_read(gp, data->phy_id & 0x1f, data->reg_num & 0x1f);
+ data->val_out = __phy_read(gp, data->phy_id & 0x1f,
+ data->reg_num & 0x1f);
rc = 0;
}
break;
@@ -2670,14 +2768,19 @@
case SIOCSMIIREG: /* Write MII PHY register. */
if (!capable(CAP_NET_ADMIN))
rc = -EPERM;
- else if (!gp->hw_running)
- rc = -EIO;
+ else if (!gp->running)
+ rc = -EAGAIN;
else {
- __phy_write(gp, data->phy_id & 0x1f, data->reg_num & 0x1f, data->val_in);
+ __phy_write(gp, data->phy_id & 0x1f, data->reg_num & 0x1f,
+ data->val_in);
rc = 0;
}
break;
};
+
+ spin_lock_irqsave(&gp->lock, flags);
+ gem_put_cell(gp);
+ spin_unlock_irqrestore(&gp->lock, flags);
up(&gp->pm_sem);
@@ -2782,6 +2885,47 @@
return 0;
}
+static void __devexit gem_remove_one(struct pci_dev *pdev)
+{
+ struct net_device *dev = pci_get_drvdata(pdev);
+
+ if (dev) {
+ struct gem *gp = dev->priv;
+
+ unregister_netdev(dev);
+
+ /* Stop the link timer */
+ del_timer_sync(&gp->link_timer);
+
+ /* We shouldn't need any locking here */
+ gem_get_cell(gp);
+
+ /* Wait for a pending reset task to complete */
+ while (gp->reset_task_pending)
+ yield();
+ flush_scheduled_work();
+
+ /* Shut the PHY down */
+ gem_stop_phy(gp, 0);
+
+ gem_put_cell(gp);
+
+ /* Make sure bus master is disabled */
+ pci_disable_device(gp->pdev);
+
+ /* Free resources */
+ pci_free_consistent(pdev,
+ sizeof(struct gem_init_block),
+ gp->init_block,
+ gp->gblock_dvma);
+ iounmap(gp->regs);
+ pci_release_regions(pdev);
+ free_netdev(dev);
+
+ pci_set_drvdata(pdev, NULL);
+ }
+}
+
static int __devinit gem_init_one(struct pci_dev *pdev,
const struct pci_device_id *ent)
{
@@ -2873,11 +3017,6 @@
gp->link_timer.function = gem_link_timer;
gp->link_timer.data = (unsigned long) gp;
- init_timer(&gp->pm_timer);
- gp->pm_timer.function = gem_pm_timer;
- gp->pm_timer.data = (unsigned long) gp;
-
- INIT_WORK(&gp->pm_task, gem_pm_task, gp);
INIT_WORK(&gp->reset_task, gem_reset_task, gp);
gp->lstate = link_down;
@@ -2893,19 +3032,18 @@
}
/* On Apple, we power the chip up now in order for check
- * invariants to work, but also because the firmware might
- * not have properly shut down the PHY.
+ * invariants to work. We also set some registers that are
+ * normally
*/
#ifdef CONFIG_PPC_PMAC
gp->of_node = pci_device_to_OF_node(pdev);
- if (pdev->vendor == PCI_VENDOR_ID_APPLE)
- gem_apple_powerup(gp);
#endif
- spin_lock_irq(&gp->lock);
- spin_lock(&gp->tx_lock);
- gem_stop(gp);
- spin_unlock(&gp->tx_lock);
- spin_unlock_irq(&gp->lock);
+
+ /* Make sure cell is enabled */
+ gem_get_cell(gp);
+
+ /* Make sure everything is stopped and in init state */
+ gem_reset(gp);
/* Fill up the mii_phy structure (even if we won't use it) */
gp->phy_mii.dev = dev;
@@ -2914,7 +3052,8 @@
/* By default, we start with autoneg */
gp->want_autoneg = 1;
-
+
+ /* Check fifo sizes, PHY type, etc... */
if (gem_check_invariants(gp)) {
err = -ENODEV;
goto err_out_iounmap;
@@ -2954,6 +3093,19 @@
dev->poll_controller = gem_poll_controller;
#endif
+ /* Set that now, in case PM kicks in now */
+ pci_set_drvdata(pdev, dev);
+
+ /* Detect & init PHY, start autoneg, we release the cell now
+ * too, it will be managed by whoever needs it
+ */
+ gem_init_phy(gp);
+
+ spin_lock_irq(&gp->lock);
+ gem_put_cell(gp);
+ spin_unlock_irq(&gp->lock);
+
+ /* Register with kernel */
if (register_netdev(dev)) {
printk(KERN_ERR PFX "Cannot register net device, "
"aborting.\n");
@@ -2968,48 +3120,22 @@
i == 5 ? ' ' : ':');
printk("\n");
- /* Detect & init PHY, start autoneg */
- spin_lock_irq(&gp->lock);
- spin_lock(&gp->tx_lock);
- gp->hw_running = 1;
- gem_init_phy(gp);
- gem_begin_auto_negotiation(gp, NULL);
- spin_unlock(&gp->tx_lock);
- spin_unlock_irq(&gp->lock);
-
if (gp->phy_type == phy_mii_mdio0 ||
gp->phy_type == phy_mii_mdio1)
printk(KERN_INFO "%s: Found %s PHY\n", dev->name,
gp->phy_mii.def ? gp->phy_mii.def->name : "no");
- pci_set_drvdata(pdev, dev);
-
/* GEM can do it all... */
dev->features |= NETIF_F_SG | NETIF_F_HW_CSUM | NETIF_F_LLTX;
if (pci_using_dac)
dev->features |= NETIF_F_HIGHDMA;
- /* Fire the PM timer that will shut us down in about 10 seconds */
- gp->pm_timer.expires = jiffies + 10*HZ;
- add_timer(&gp->pm_timer);
-
return 0;
err_out_free_consistent:
- pci_free_consistent(pdev,
- sizeof(struct gem_init_block),
- gp->init_block,
- gp->gblock_dvma);
-
+ gem_remove_one(pdev);
err_out_iounmap:
- down(&gp->pm_sem);
- /* Stop the PM timer & task */
- del_timer_sync(&gp->pm_timer);
- flush_scheduled_work();
- if (gp->hw_running)
- gem_shutdown(gp);
- up(&gp->pm_sem);
-
+ gem_put_cell(gp);
iounmap(gp->regs);
err_out_free_res:
@@ -3023,34 +3149,6 @@
}
-static void __devexit gem_remove_one(struct pci_dev *pdev)
-{
- struct net_device *dev = pci_get_drvdata(pdev);
-
- if (dev) {
- struct gem *gp = dev->priv;
-
- unregister_netdev(dev);
-
- down(&gp->pm_sem);
- /* Stop the PM timer & task */
- del_timer_sync(&gp->pm_timer);
- flush_scheduled_work();
- if (gp->hw_running)
- gem_shutdown(gp);
- up(&gp->pm_sem);
-
- pci_free_consistent(pdev,
- sizeof(struct gem_init_block),
- gp->init_block,
- gp->gblock_dvma);
- iounmap(gp->regs);
- pci_release_regions(pdev);
- free_netdev(dev);
-
- pci_set_drvdata(pdev, NULL);
- }
-}
static struct pci_driver gem_driver = {
.name = GEM_MODULE_NAME,
diff -ur a/drivers/net/sungem.h b/drivers/net/sungem.h
--- a/drivers/net/sungem.h 2004-10-18 23:54:32.000000000 +0200
+++ b/drivers/net/sungem.h 2004-11-26 17:36:42.000000000 +0100
@@ -170,6 +170,27 @@
* them later. -DaveM
*/
+/* WakeOnLan Registers */
+#define WOL_MATCH0 0x3000UL
+#define WOL_MATCH1 0x3004UL
+#define WOL_MATCH2 0x3008UL
+#define WOL_MCOUNT 0x300CUL
+#define WOL_WAKECSR 0x3010UL
+
+/* WOL Match count register
+ */
+#define WOL_MCOUNT_N 0x00000010
+#define WOL_MCOUNT_M 0x00000000 /* 0 << 8 */
+
+#define WOL_WAKECSR_ENABLE 0x00000001
+#define WOL_WAKECSR_MII 0x00000002
+#define WOL_WAKECSR_SEEN 0x00000004
+#define WOL_WAKECSR_FILT_UCAST 0x00000008
+#define WOL_WAKECSR_FILT_MCAST 0x00000010
+#define WOL_WAKECSR_FILT_BCAST 0x00000020
+#define WOL_WAKECSR_FILT_SEEN 0x00000040
+
+
/* Receive DMA Registers */
#define RXDMA_CFG 0x4000UL /* RX Configuration Register */
#define RXDMA_DBLOW 0x4004UL /* RX Descriptor Base Low */
@@ -276,6 +297,9 @@
*/
/* MAC Registers */
+#define MAC_WOL_MAGIC0 0x3000UL /* six address bytes */
+#define MAC_WOL_MAGIC1 0x3004UL
+#define MAC_WOL_MAGIC2 0x3008UL
#define MAC_TXRST 0x6000UL /* TX MAC Software Reset Command*/
#define MAC_RXRST 0x6004UL /* RX MAC Software Reset Command*/
#define MAC_SNDPAUSE 0x6008UL /* Send Pause Command Register */
@@ -750,6 +774,17 @@
#define PCS_SCTRL_TXZ 0x0000c000 /* PLL input to Serialink */
#define PCS_SCTRL_TXP 0x00030000 /* PLL input to Serialink */
+/* Pattern Match Count register. */
+#define PAT_MATCH_CFG 0x300CUL
+#define PAT_MATCH_N 0x0010UL
+#define PAT_MATCH_M 0x0000UL /* Darwin source notes (0<<8) !?*/
+
+/* Wake on Lan CSR Register */
+#define WOL_CSR_CFG 0x3010UL
+#define WOL_ENABLE 0x0001UL
+#define WOL_MODE_MII 0x0002UL
+
+
/* Shared Output Select Register. For test and debug, allows multiplexing
* test outputs into the PROM address pins. Set to zero for normal
* operation.
@@ -961,11 +996,12 @@
/* Set when chip is actually in operational state
* (ie. not power managed)
*/
- int hw_running;
- int opened;
+ int asleep; /* chip asleep, protected by pm_sem */
+ int asleep_wol; /* was asleep with WOL enabled */
+ int cell_enabled; /* cell enable count, protected by lock */
+ int opened; /* driver opened, protected by pm_sem */
+ int running; /* chip running, protected by lock */
struct semaphore pm_sem;
- struct work_struct pm_task;
- struct timer_list pm_timer;
struct gem_init_block *init_block;
diff -ur a/drivers/net/sungem_phy.c b/drivers/net/sungem_phy.c
--- a/drivers/net/sungem_phy.c 2004-11-26 20:09:44.801523912 +0100
+++ b/drivers/net/sungem_phy.c 2004-11-26 17:49:31.000000000 +0100
@@ -98,25 +98,15 @@
data &= ~MII_BCM5201_MULTIPHY_SUPERISOLATE;
phy_write(phy, MII_BCM5201_MULTIPHY, data);
+ phy_write(phy, MII_BCM5201_INTERRUPT, 0);
+
return 0;
}
-static int bcm5201_suspend(struct mii_phy* phy, int wol_options)
+static int bcm5201_suspend(struct mii_phy* phy)
{
- if (!wol_options)
- phy_write(phy, MII_BCM5201_INTERRUPT, 0);
-
- /* Here's a strange hack used by both MacOS 9 and X */
- phy_write(phy, MII_LPA, phy_read(phy, MII_LPA));
-
- if (!wol_options) {
-#if 0 /* Commented out in Darwin... someone has those dawn docs ? */
- u16 val = phy_read(phy, MII_BCM5201_AUXMODE2)
- phy_write(phy, MII_BCM5201_AUXMODE2,
- val & ~MII_BCM5201_AUXMODE2_LOWPOWER);
-#endif
- phy_write(phy, MII_BCM5201_MULTIPHY, MII_BCM5201_MULTIPHY_SUPERISOLATE);
- }
+ phy_write(phy, MII_BCM5201_INTERRUPT, 0);
+ phy_write(phy, MII_BCM5201_MULTIPHY, MII_BCM5201_MULTIPHY_SUPERISOLATE);
return 0;
}
@@ -144,9 +134,10 @@
return 0;
}
-static int bcm5221_suspend(struct mii_phy* phy, int wol_options)
+static int bcm5221_suspend(struct mii_phy* phy)
{
u16 data;
+
data = phy_read(phy, MII_BCM5221_TEST);
phy_write(phy, MII_BCM5221_TEST,
data | MII_BCM5221_TEST_ENABLE_SHADOWS);
@@ -187,7 +178,7 @@
return 0;
}
-static int bcm5400_suspend(struct mii_phy* phy, int wol_options)
+static int bcm5400_suspend(struct mii_phy* phy)
{
#if 0 /* Commented out in Darwin... someone has those dawn docs ? */
phy_write(phy, MII_BMCR, BMCR_PDOWN);
@@ -243,7 +234,7 @@
return 0;
}
-static int bcm5401_suspend(struct mii_phy* phy, int wol_options)
+static int bcm5401_suspend(struct mii_phy* phy)
{
#if 0 /* Commented out in Darwin... someone has those dawn docs ? */
phy_write(phy, MII_BMCR, BMCR_PDOWN);
@@ -280,7 +271,7 @@
return 0;
}
-static int bcm5411_suspend(struct mii_phy* phy, int wol_options)
+static int bcm5411_suspend(struct mii_phy* phy)
{
phy_write(phy, MII_BMCR, BMCR_PDOWN);
diff -ur a/drivers/net/sungem_phy.h b/drivers/net/sungem_phy.h
--- a/drivers/net/sungem_phy.h 2004-11-26 20:09:44.802523760 +0100
+++ b/drivers/net/sungem_phy.h 2004-11-26 17:36:42.000000000 +0100
@@ -7,7 +7,7 @@
struct mii_phy_ops
{
int (*init)(struct mii_phy *phy);
- int (*suspend)(struct mii_phy *phy, int wol_options);
+ int (*suspend)(struct mii_phy *phy);
int (*setup_aneg)(struct mii_phy *phy, u32 advertise);
int (*setup_forced)(struct mii_phy *phy, int speed, int fd);
int (*poll_link)(struct mii_phy *phy);
^ permalink raw reply [flat|nested] only message in thread
only message in thread, other threads:[~2004-11-27 16:59 UTC | newest]
Thread overview: (only message) (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2004-11-27 16:58 [PATCH] test patch, sungem wake-on-lan for 2.6.9+sleep Colin Leroy
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).