Re: [PATCH] sungem: add support for G5 PowerMac, some PM fixes

From: Benjamin Herrenschmidt
Date: Thu Jan 22 2004 - 18:17:29 EST


On Thu, 2004-01-22 at 17:08, David S. Miller wrote:
> On Thu, 22 Jan 2004 16:50:43 +1100
> Benjamin Herrenschmidt <benh@xxxxxxxxxxxxxxxxxxx> wrote:
>
> > + if (gp->pdev->device == PCI_DEVICE_ID_APPLE_K2_GMAC)
> > + break;
> > +
> > + for (i = 0; i < 32; i++) {
>
> Please fix this indentation, and I'll happily apply this ;-)
>
> I assume you'll have a 2.4.x variant of this patch too?

Ohh, this was just a tab vs. space issue actually, the resulting indent
was fine ;) Anyway, here's the fixed version. The 2.4 backport will
come next week hopefully when I'll walk through all my pending 2.4
stuffs, I want to dbl check a few things in it first (and I'm not yet
100% certain I will merge the G5 support in 2.4 upstream anyway).

Note: the new PCI ID isn't yet in Linus tree, but it's in the patches
I'm currently sending to Andrew.

Ben.

diff -urN linux-2.5-merge/drivers/net/sungem.c linuxppc-2.5-benh/drivers/net/sungem.c
--- linux-2.5-merge/drivers/net/sungem.c 2004-01-23 10:10:00.836189096 +1100
+++ linuxppc-2.5-benh/drivers/net/sungem.c 2004-01-23 10:10:53.087245728 +1100
@@ -103,6 +103,8 @@
PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL },
{ PCI_VENDOR_ID_APPLE, PCI_DEVICE_ID_APPLE_UNI_N_GMAC2,
PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL },
+ { PCI_VENDOR_ID_APPLE, PCI_DEVICE_ID_APPLE_K2_GMAC,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL },
{0, }
};

@@ -778,6 +780,10 @@
struct gem *gp = dev->priv;
u32 gem_status = readl(gp->regs + GREG_STAT);

+ /* Swallow interrupts when shutting the chip down */
+ if (gp->hw_running == 0)
+ goto out;
+
spin_lock(&gp->lock);

if (gem_status & GREG_STAT_ABNORMAL) {
@@ -1240,6 +1246,12 @@
gp->lstate = link_force_ok;
return 0;
case link_aneg:
+ /* We try forced modes after a failed aneg only on PHYs that don't
+ * have "magic_aneg" bit set, which means they internally do the
+ * while forced-mode thingy. On these, we just restart aneg
+ */
+ if (gp->phy_mii.def->magic_aneg)
+ return 1;
if (netif_msg_link(gp))
printk(KERN_INFO "%s: switching to forced 100bt\n",
gp->dev->name);
@@ -1497,18 +1509,26 @@
* to schedule instead
*/
pmac_call_feature(PMAC_FTR_GMAC_PHY_RESET, gp->of_node, 0, 0);
- mdelay(10);
for (j = 0; j < 3; j++) {
/* Some PHYs used by apple have problem getting back to us,
- * we _know_ it's actually at addr 0, that's a hack, but
+ * 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.
*/
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)
+ break;
+
for (i = 0; i < 32; i++) {
gp->mii_phy_addr = i;
if (phy_read(gp, MII_BMCR) != 0xffff)
@@ -1770,6 +1790,8 @@
/* Must be invoked under gp->lock. */
static void gem_init_pause_thresholds(struct gem *gp)
{
+ u32 cfg;
+
/* Calculate pause thresholds. Setting the OFF threshold to the
* full RX fifo size effectively disables PAUSE generation which
* is what we do for 10/100 only GEMs which have FIFOs too small
@@ -1786,17 +1808,28 @@
gp->rx_pause_on = on;
}

- {
- u32 cfg;

- cfg = 0;
+ /* Configure the chip "burst" DMA mode & enable some
+ * HW bug fixes on Apple version
+ */
+ cfg = 0;
+ if (gp->pdev->vendor == PCI_VENDOR_ID_APPLE)
+ cfg |= GREG_CFG_RONPAULBIT | GREG_CFG_ENBUG2FIX;
#if !defined(CONFIG_SPARC64) && !defined(CONFIG_ALPHA)
- cfg |= GREG_CFG_IBURST;
+ cfg |= GREG_CFG_IBURST;
#endif
- cfg |= ((31 << 1) & GREG_CFG_TXDMALIM);
- cfg |= ((31 << 6) & GREG_CFG_RXDMALIM);
+ cfg |= ((31 << 1) & GREG_CFG_TXDMALIM);
+ cfg |= ((31 << 6) & GREG_CFG_RXDMALIM);
+ writel(cfg, gp->regs + GREG_CFG);
+
+ /* If Infinite Burst didn't stick, then use different
+ * thresholds (and Apple bug fixes don't exist)
+ */
+ if (readl(gp->regs + GREG_CFG) & GREG_CFG_IBURST) {
+ cfg = ((2 << 1) & GREG_CFG_TXDMALIM);
+ cfg = ((8 << 6) & GREG_CFG_RXDMALIM);
writel(cfg, gp->regs + GREG_CFG);
- }
+ }
}

static int gem_check_invariants(struct gem *gp)
@@ -1931,18 +1964,10 @@
u16 cmd;
u32 mif_cfg;

+ mb();
pmac_call_feature(PMAC_FTR_GMAC_ENABLE, gp->of_node, 0, 1);

- current->state = TASK_UNINTERRUPTIBLE;
- schedule_timeout((21 * HZ) / 1000);
-
- pci_read_config_word(gp->pdev, PCI_COMMAND, &cmd);
- cmd |= PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER | PCI_COMMAND_INVALIDATE;
- pci_write_config_word(gp->pdev, PCI_COMMAND, cmd);
- pci_write_config_byte(gp->pdev, PCI_LATENCY_TIMER, 6);
- pci_write_config_byte(gp->pdev, PCI_CACHE_LINE_SIZE, 8);
-
- mdelay(1);
+ udelay(3);

mif_cfg = readl(gp->regs + MIF_CFG);
mif_cfg &= ~(MIF_CFG_PSELECT|MIF_CFG_POLL|MIF_CFG_BBMODE|MIF_CFG_MDI1);
@@ -1950,8 +1975,6 @@
writel(mif_cfg, gp->regs + MIF_CFG);
writel(PCS_DMODE_MGM, gp->regs + PCS_DMODE);
writel(MAC_XIFCFG_OE, gp->regs + MAC_XIFCFG);
-
- mdelay(1);
}

/* Turn off the chip's clock */
@@ -1962,10 +1985,17 @@

#endif /* CONFIG_PPC_PMAC */

-/* Must be invoked under gp->lock. */
+/* Must be invoked with no lock held. */
static void gem_stop_phy(struct gem *gp)
{
u32 mifcfg;
+ unsigned long flags;
+
+ /* Let the chip settle down a bit, it seems that helps
+ * for sleep mode on some models
+ */
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ schedule_timeout(HZ/100);

/* Make sure we aren't polling PHY status change. We
* don't currently use that feature though
@@ -1976,17 +2006,28 @@

if (gp->wake_on_lan) {
/* Setup wake-on-lan */
- } else
+ } else {
writel(0, gp->regs + MAC_RXCFG);
+ (void)readl(gp->regs + MAC_RXCFG);
+ /* Machine sleep will die in strange ways if we
+ * dont wait a bit here, looks like the chip takes
+ * some time to really shut down
+ */
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ schedule_timeout(HZ/100);
+ }
+
writel(0, gp->regs + MAC_TXCFG);
writel(0, gp->regs + MAC_XIFCFG);
writel(0, gp->regs + TXDMA_CFG);
writel(0, gp->regs + RXDMA_CFG);

if (!gp->wake_on_lan) {
+ spin_lock_irqsave(&gp->lock, flags);
gem_stop(gp);
writel(MAC_TXRST_CMD, gp->regs + MAC_TXRST);
writel(MAC_RXRST_CMD, gp->regs + MAC_RXRST);
+ spin_unlock_irqrestore(&gp->lock, flags);
}

if (found_mii_phy(gp) && gp->phy_mii.def->ops->suspend)
@@ -2008,31 +2049,33 @@
/* Shut down the chip, must be called with pm_sem held. */
static void gem_shutdown(struct gem *gp)
{
- /* Make us not-running to avoid timers respawning */
+ /* Make us not-running to avoid timers respawning
+ * and swallow irqs
+ */
gp->hw_running = 0;
+ wmb();

/* Stop the link timer */
del_timer_sync(&gp->link_timer);

/* Stop the reset task */
while (gp->reset_task_pending)
- schedule();
+ yield();

/* Actually stop the chip */
- spin_lock_irq(&gp->lock);
if (gp->pdev->vendor == PCI_VENDOR_ID_APPLE) {
gem_stop_phy(gp);

- spin_unlock_irq(&gp->lock);
-
#ifdef CONFIG_PPC_PMAC
/* Power down the chip */
gem_apple_powerdown(gp);
#endif /* CONFIG_PPC_PMAC */
- } else {
- gem_stop(gp);
+ } else{
+ unsigned long flags;

- spin_unlock_irq(&gp->lock);
+ spin_lock_irqsave(&gp->lock, flags);
+ gem_stop(gp);
+ spin_unlock_irqrestore(&gp->lock, flags);
}
}

@@ -2692,6 +2735,7 @@
* not have properly shut down the PHY.
*/
#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
@@ -2725,9 +2769,6 @@
goto err_out_iounmap;
}

-#ifdef CONFIG_PPC_PMAC
- gp->of_node = pci_device_to_OF_node(pdev);
-#endif
if (gem_get_device_address(gp))
goto err_out_free_consistent;

diff -urN linux-2.5-merge/drivers/net/sungem.h linuxppc-2.5-benh/drivers/net/sungem.h
--- linux-2.5-merge/drivers/net/sungem.h 2004-01-23 10:10:00.853186512 +1100
+++ linuxppc-2.5-benh/drivers/net/sungem.h 2004-01-23 10:10:15.303989656 +1100
@@ -28,6 +28,9 @@
#define GREG_CFG_IBURST 0x00000001 /* Infinite Burst */
#define GREG_CFG_TXDMALIM 0x0000003e /* TX DMA grant limit */
#define GREG_CFG_RXDMALIM 0x000007c0 /* RX DMA grant limit */
+#define GREG_CFG_RONPAULBIT 0x00000800 /* Use mem read multiple for PCI read
+ * after infinite burst (Apple) */
+#define GREG_CFG_ENBUG2FIX 0x00001000 /* Fix Rx hang after overflow */

/* Global Interrupt Status Register.
*
diff -urN linux-2.5-merge/drivers/net/sungem_phy.c linuxppc-2.5-benh/drivers/net/sungem_phy.c
--- linux-2.5-merge/drivers/net/sungem_phy.c 2004-01-23 10:10:00.866184536 +1100
+++ linuxppc-2.5-benh/drivers/net/sungem_phy.c 2004-01-23 10:10:15.323986616 +1100
@@ -72,7 +72,7 @@
int limit = 10000;

val = __phy_read(phy, phy_id, MII_BMCR);
- val &= ~BMCR_ISOLATE;
+ val &= ~(BMCR_ISOLATE | BMCR_PDOWN);
val |= BMCR_RESET;
__phy_write(phy, phy_id, MII_BMCR, val);

@@ -157,7 +157,7 @@
data |= MII_BCM5400_GB_CONTROL_FULLDUPLEXCAP;
phy_write(phy, MII_BCM5400_GB_CONTROL, data);

- mdelay(10);
+ udelay(100);

/* Reset and configure cascaded 10/100 PHY */
(void)reset_one_mii_phy(phy, 0x1f);
@@ -217,7 +217,7 @@
data |= MII_BCM5400_GB_CONTROL_FULLDUPLEXCAP;
phy_write(phy, MII_BCM5400_GB_CONTROL, data);

- mdelay(10);
+ udelay(10);

/* Reset and configure cascaded 10/100 PHY */
(void)reset_one_mii_phy(phy, 0x1f);
@@ -258,7 +258,7 @@
data |= MII_BCM5400_GB_CONTROL_FULLDUPLEXCAP;
phy_write(phy, MII_BCM5400_GB_CONTROL, data);

- mdelay(10);
+ udelay(10);

/* Reset and configure cascaded 10/100 PHY */
(void)reset_one_mii_phy(phy, 0x1f);
@@ -302,6 +302,15 @@
return 0;
}

+static int bcm5421k2_init(struct mii_phy* phy)
+{
+ /* Init code borrowed from OF */
+ phy_write(phy, 4, 0x01e1);
+ phy_write(phy, 9, 0x0300);
+
+ return 0;
+}
+
static int bcm54xx_setup_aneg(struct mii_phy *phy, u32 advertise)
{
u16 ctl, adv;
@@ -647,7 +656,7 @@
.phy_id_mask = 0xfffffff0,
.name = "BCM5201",
.features = MII_BASIC_FEATURES,
- .magic_aneg = 0,
+ .magic_aneg = 1,
.ops = &bcm5201_phy_ops
};

@@ -666,7 +675,7 @@
.phy_id_mask = 0xfffffff0,
.name = "BCM5221",
.features = MII_BASIC_FEATURES,
- .magic_aneg = 0,
+ .magic_aneg = 1,
.ops = &bcm5221_phy_ops
};

@@ -746,6 +755,25 @@
.ops = &bcm5421_phy_ops
};

+/* Broadcom BCM 5421 built-in K2 */
+static struct mii_phy_ops bcm5421k2_phy_ops = {
+ .init = bcm5421k2_init,
+ .suspend = bcm5411_suspend,
+ .setup_aneg = bcm54xx_setup_aneg,
+ .setup_forced = bcm54xx_setup_forced,
+ .poll_link = genmii_poll_link,
+ .read_link = bcm54xx_read_link,
+};
+
+static struct mii_phy_def bcm5421k2_phy_def = {
+ .phy_id = 0x002062e0,
+ .phy_id_mask = 0xfffffff0,
+ .name = "BCM5421-K2",
+ .features = MII_GBIT_FEATURES,
+ .magic_aneg = 1,
+ .ops = &bcm5421k2_phy_ops
+};
+
/* Marvell 88E1101 (Apple seem to deal with 2 different revs,
* I masked out the 8 last bits to get both, but some specs
* would be useful here) --BenH.
@@ -790,6 +818,7 @@
&bcm5401_phy_def,
&bcm5411_phy_def,
&bcm5421_phy_def,
+ &bcm5421k2_phy_def,
&marvell_phy_def,
&genmii_phy_def,
NULL
@@ -813,8 +842,8 @@
goto fail;

/* Read ID and find matching entry */
- id = (phy_read(phy, MII_PHYSID1) << 16 | phy_read(phy, MII_PHYSID2))
- & 0xfffffff0;
+ id = (phy_read(phy, MII_PHYSID1) << 16 | phy_read(phy, MII_PHYSID2));
+ printk(KERN_DEBUG "PHY ID: %x, addr: %x\n", id, mii_id);
for (i=0; (def = mii_phy_table[i]) != NULL; i++)
if ((id & def->phy_id_mask) == def->phy_id)
break;


-
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/