|
@@ -314,6 +314,16 @@ static u32 tg3_read32(struct tg3 *tp, u32 off)
|
|
|
return (readl(tp->regs + off));
|
|
|
}
|
|
|
|
|
|
+static void tg3_ape_write32(struct tg3 *tp, u32 off, u32 val)
|
|
|
+{
|
|
|
+ writel(val, tp->aperegs + off);
|
|
|
+}
|
|
|
+
|
|
|
+static u32 tg3_ape_read32(struct tg3 *tp, u32 off)
|
|
|
+{
|
|
|
+ return (readl(tp->aperegs + off));
|
|
|
+}
|
|
|
+
|
|
|
static void tg3_write_indirect_reg32(struct tg3 *tp, u32 off, u32 val)
|
|
|
{
|
|
|
unsigned long flags;
|
|
@@ -500,6 +510,73 @@ static void tg3_read_mem(struct tg3 *tp, u32 off, u32 *val)
|
|
|
spin_unlock_irqrestore(&tp->indirect_lock, flags);
|
|
|
}
|
|
|
|
|
|
+static void tg3_ape_lock_init(struct tg3 *tp)
|
|
|
+{
|
|
|
+ int i;
|
|
|
+
|
|
|
+ /* Make sure the driver hasn't any stale locks. */
|
|
|
+ for (i = 0; i < 8; i++)
|
|
|
+ tg3_ape_write32(tp, TG3_APE_LOCK_GRANT + 4 * i,
|
|
|
+ APE_LOCK_GRANT_DRIVER);
|
|
|
+}
|
|
|
+
|
|
|
+static int tg3_ape_lock(struct tg3 *tp, int locknum)
|
|
|
+{
|
|
|
+ int i, off;
|
|
|
+ int ret = 0;
|
|
|
+ u32 status;
|
|
|
+
|
|
|
+ if (!(tp->tg3_flags3 & TG3_FLG3_ENABLE_APE))
|
|
|
+ return 0;
|
|
|
+
|
|
|
+ switch (locknum) {
|
|
|
+ case TG3_APE_LOCK_MEM:
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ return -EINVAL;
|
|
|
+ }
|
|
|
+
|
|
|
+ off = 4 * locknum;
|
|
|
+
|
|
|
+ tg3_ape_write32(tp, TG3_APE_LOCK_REQ + off, APE_LOCK_REQ_DRIVER);
|
|
|
+
|
|
|
+ /* Wait for up to 1 millisecond to acquire lock. */
|
|
|
+ for (i = 0; i < 100; i++) {
|
|
|
+ status = tg3_ape_read32(tp, TG3_APE_LOCK_GRANT + off);
|
|
|
+ if (status == APE_LOCK_GRANT_DRIVER)
|
|
|
+ break;
|
|
|
+ udelay(10);
|
|
|
+ }
|
|
|
+
|
|
|
+ if (status != APE_LOCK_GRANT_DRIVER) {
|
|
|
+ /* Revoke the lock request. */
|
|
|
+ tg3_ape_write32(tp, TG3_APE_LOCK_GRANT + off,
|
|
|
+ APE_LOCK_GRANT_DRIVER);
|
|
|
+
|
|
|
+ ret = -EBUSY;
|
|
|
+ }
|
|
|
+
|
|
|
+ return ret;
|
|
|
+}
|
|
|
+
|
|
|
+static void tg3_ape_unlock(struct tg3 *tp, int locknum)
|
|
|
+{
|
|
|
+ int off;
|
|
|
+
|
|
|
+ if (!(tp->tg3_flags3 & TG3_FLG3_ENABLE_APE))
|
|
|
+ return;
|
|
|
+
|
|
|
+ switch (locknum) {
|
|
|
+ case TG3_APE_LOCK_MEM:
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ off = 4 * locknum;
|
|
|
+ tg3_ape_write32(tp, TG3_APE_LOCK_GRANT + off, APE_LOCK_GRANT_DRIVER);
|
|
|
+}
|
|
|
+
|
|
|
static void tg3_disable_ints(struct tg3 *tp)
|
|
|
{
|
|
|
tw32(TG3PCI_MISC_HOST_CTRL,
|
|
@@ -1448,7 +1525,8 @@ static int tg3_set_power_state(struct tg3 *tp, pci_power_t state)
|
|
|
}
|
|
|
|
|
|
if (!(tp->tg3_flags & TG3_FLAG_WOL_ENABLE) &&
|
|
|
- !(tp->tg3_flags & TG3_FLAG_ENABLE_ASF))
|
|
|
+ !(tp->tg3_flags & TG3_FLAG_ENABLE_ASF) &&
|
|
|
+ !(tp->tg3_flags3 & TG3_FLG3_ENABLE_APE))
|
|
|
tg3_power_down_phy(tp);
|
|
|
|
|
|
tg3_frob_aux_power(tp);
|
|
@@ -4726,6 +4804,80 @@ static void tg3_disable_nvram_access(struct tg3 *tp)
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+static void tg3_ape_send_event(struct tg3 *tp, u32 event)
|
|
|
+{
|
|
|
+ int i;
|
|
|
+ u32 apedata;
|
|
|
+
|
|
|
+ apedata = tg3_ape_read32(tp, TG3_APE_SEG_SIG);
|
|
|
+ if (apedata != APE_SEG_SIG_MAGIC)
|
|
|
+ return;
|
|
|
+
|
|
|
+ apedata = tg3_ape_read32(tp, TG3_APE_FW_STATUS);
|
|
|
+ if (apedata != APE_FW_STATUS_READY)
|
|
|
+ return;
|
|
|
+
|
|
|
+ /* Wait for up to 1 millisecond for APE to service previous event. */
|
|
|
+ for (i = 0; i < 10; i++) {
|
|
|
+ if (tg3_ape_lock(tp, TG3_APE_LOCK_MEM))
|
|
|
+ return;
|
|
|
+
|
|
|
+ apedata = tg3_ape_read32(tp, TG3_APE_EVENT_STATUS);
|
|
|
+
|
|
|
+ if (!(apedata & APE_EVENT_STATUS_EVENT_PENDING))
|
|
|
+ tg3_ape_write32(tp, TG3_APE_EVENT_STATUS,
|
|
|
+ event | APE_EVENT_STATUS_EVENT_PENDING);
|
|
|
+
|
|
|
+ tg3_ape_unlock(tp, TG3_APE_LOCK_MEM);
|
|
|
+
|
|
|
+ if (!(apedata & APE_EVENT_STATUS_EVENT_PENDING))
|
|
|
+ break;
|
|
|
+
|
|
|
+ udelay(100);
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!(apedata & APE_EVENT_STATUS_EVENT_PENDING))
|
|
|
+ tg3_ape_write32(tp, TG3_APE_EVENT, APE_EVENT_1);
|
|
|
+}
|
|
|
+
|
|
|
+static void tg3_ape_driver_state_change(struct tg3 *tp, int kind)
|
|
|
+{
|
|
|
+ u32 event;
|
|
|
+ u32 apedata;
|
|
|
+
|
|
|
+ if (!(tp->tg3_flags3 & TG3_FLG3_ENABLE_APE))
|
|
|
+ return;
|
|
|
+
|
|
|
+ switch (kind) {
|
|
|
+ case RESET_KIND_INIT:
|
|
|
+ tg3_ape_write32(tp, TG3_APE_HOST_SEG_SIG,
|
|
|
+ APE_HOST_SEG_SIG_MAGIC);
|
|
|
+ tg3_ape_write32(tp, TG3_APE_HOST_SEG_LEN,
|
|
|
+ APE_HOST_SEG_LEN_MAGIC);
|
|
|
+ apedata = tg3_ape_read32(tp, TG3_APE_HOST_INIT_COUNT);
|
|
|
+ tg3_ape_write32(tp, TG3_APE_HOST_INIT_COUNT, ++apedata);
|
|
|
+ tg3_ape_write32(tp, TG3_APE_HOST_DRIVER_ID,
|
|
|
+ APE_HOST_DRIVER_ID_MAGIC);
|
|
|
+ tg3_ape_write32(tp, TG3_APE_HOST_BEHAVIOR,
|
|
|
+ APE_HOST_BEHAV_NO_PHYLOCK);
|
|
|
+
|
|
|
+ event = APE_EVENT_STATUS_STATE_START;
|
|
|
+ break;
|
|
|
+ case RESET_KIND_SHUTDOWN:
|
|
|
+ event = APE_EVENT_STATUS_STATE_UNLOAD;
|
|
|
+ break;
|
|
|
+ case RESET_KIND_SUSPEND:
|
|
|
+ event = APE_EVENT_STATUS_STATE_SUSPEND;
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ event |= APE_EVENT_STATUS_DRIVER_EVNT | APE_EVENT_STATUS_STATE_CHNGE;
|
|
|
+
|
|
|
+ tg3_ape_send_event(tp, event);
|
|
|
+}
|
|
|
+
|
|
|
/* tp->lock is held. */
|
|
|
static void tg3_write_sig_pre_reset(struct tg3 *tp, int kind)
|
|
|
{
|
|
@@ -4753,6 +4905,10 @@ static void tg3_write_sig_pre_reset(struct tg3 *tp, int kind)
|
|
|
break;
|
|
|
};
|
|
|
}
|
|
|
+
|
|
|
+ if (kind == RESET_KIND_INIT ||
|
|
|
+ kind == RESET_KIND_SUSPEND)
|
|
|
+ tg3_ape_driver_state_change(tp, kind);
|
|
|
}
|
|
|
|
|
|
/* tp->lock is held. */
|
|
@@ -4774,6 +4930,9 @@ static void tg3_write_sig_post_reset(struct tg3 *tp, int kind)
|
|
|
break;
|
|
|
};
|
|
|
}
|
|
|
+
|
|
|
+ if (kind == RESET_KIND_SHUTDOWN)
|
|
|
+ tg3_ape_driver_state_change(tp, kind);
|
|
|
}
|
|
|
|
|
|
/* tp->lock is held. */
|
|
@@ -4864,6 +5023,10 @@ static void tg3_restore_pci_state(struct tg3 *tp)
|
|
|
if (tp->pci_chip_rev_id == CHIPREV_ID_5704_A0 &&
|
|
|
(tp->tg3_flags & TG3_FLAG_PCIX_MODE))
|
|
|
val |= PCISTATE_RETRY_SAME_DMA;
|
|
|
+ /* Allow reads and writes to the APE register and memory space. */
|
|
|
+ if (tp->tg3_flags3 & TG3_FLG3_ENABLE_APE)
|
|
|
+ val |= PCISTATE_ALLOW_APE_CTLSPC_WR |
|
|
|
+ PCISTATE_ALLOW_APE_SHMEM_WR;
|
|
|
pci_write_config_dword(tp->pdev, TG3PCI_PCISTATE, val);
|
|
|
|
|
|
pci_write_config_dword(tp->pdev, TG3PCI_COMMAND, tp->pci_cmd);
|
|
@@ -5092,7 +5255,8 @@ static int tg3_chip_reset(struct tg3 *tp)
|
|
|
/* tp->lock is held. */
|
|
|
static void tg3_stop_fw(struct tg3 *tp)
|
|
|
{
|
|
|
- if (tp->tg3_flags & TG3_FLAG_ENABLE_ASF) {
|
|
|
+ if ((tp->tg3_flags & TG3_FLAG_ENABLE_ASF) &&
|
|
|
+ !(tp->tg3_flags3 & TG3_FLG3_ENABLE_APE)) {
|
|
|
u32 val;
|
|
|
int i;
|
|
|
|
|
@@ -6173,6 +6337,16 @@ static int tg3_reset_hw(struct tg3 *tp, int reset_phy)
|
|
|
tw32(TG3PCI_PCISTATE, val);
|
|
|
}
|
|
|
|
|
|
+ if (tp->tg3_flags3 & TG3_FLG3_ENABLE_APE) {
|
|
|
+ /* Allow reads and writes to the
|
|
|
+ * APE register and memory space.
|
|
|
+ */
|
|
|
+ val = tr32(TG3PCI_PCISTATE);
|
|
|
+ val |= PCISTATE_ALLOW_APE_CTLSPC_WR |
|
|
|
+ PCISTATE_ALLOW_APE_SHMEM_WR;
|
|
|
+ tw32(TG3PCI_PCISTATE, val);
|
|
|
+ }
|
|
|
+
|
|
|
if (GET_CHIP_REV(tp->pci_chip_rev_id) == CHIPREV_5704_BX) {
|
|
|
/* Enable some hw fixes. */
|
|
|
val = tr32(TG3PCI_MSI_DATA);
|
|
@@ -6780,6 +6954,10 @@ static int tg3_reset_hw(struct tg3 *tp, int reset_phy)
|
|
|
break;
|
|
|
};
|
|
|
|
|
|
+ /* Write our heartbeat update interval to APE. */
|
|
|
+ tg3_ape_write32(tp, TG3_APE_HOST_HEARTBEAT_INT_MS,
|
|
|
+ APE_HOST_HEARTBEAT_INT_DISABLE);
|
|
|
+
|
|
|
tg3_write_sig_post_reset(tp, RESET_KIND_INIT);
|
|
|
|
|
|
return 0;
|
|
@@ -10302,6 +10480,8 @@ static void __devinit tg3_get_eeprom_hw_cfg(struct tg3 *tp)
|
|
|
if (tp->tg3_flags2 & TG3_FLG2_5750_PLUS)
|
|
|
tp->tg3_flags2 |= TG3_FLG2_ASF_NEW_HANDSHAKE;
|
|
|
}
|
|
|
+ if (nic_cfg & NIC_SRAM_DATA_CFG_APE_ENABLE)
|
|
|
+ tp->tg3_flags3 |= TG3_FLG3_ENABLE_APE;
|
|
|
if (tp->tg3_flags2 & TG3_FLG2_ANY_SERDES &&
|
|
|
!(nic_cfg & NIC_SRAM_DATA_CFG_FIBER_WOL))
|
|
|
tp->tg3_flags &= ~TG3_FLAG_WOL_CAP;
|
|
@@ -10334,7 +10514,8 @@ static int __devinit tg3_phy_probe(struct tg3 *tp)
|
|
|
* firwmare access to the PHY hardware.
|
|
|
*/
|
|
|
err = 0;
|
|
|
- if (tp->tg3_flags & TG3_FLAG_ENABLE_ASF) {
|
|
|
+ if ((tp->tg3_flags & TG3_FLAG_ENABLE_ASF) ||
|
|
|
+ (tp->tg3_flags3 & TG3_FLG3_ENABLE_APE)) {
|
|
|
hw_phy_id = hw_phy_id_masked = PHY_ID_INVALID;
|
|
|
} else {
|
|
|
/* Now read the physical PHY_ID from the chip and verify
|
|
@@ -10381,6 +10562,7 @@ static int __devinit tg3_phy_probe(struct tg3 *tp)
|
|
|
}
|
|
|
|
|
|
if (!(tp->tg3_flags2 & TG3_FLG2_ANY_SERDES) &&
|
|
|
+ !(tp->tg3_flags3 & TG3_FLG3_ENABLE_APE) &&
|
|
|
!(tp->tg3_flags & TG3_FLAG_ENABLE_ASF)) {
|
|
|
u32 bmsr, adv_reg, tg3_ctrl, mask;
|
|
|
|
|
@@ -10972,6 +11154,16 @@ static int __devinit tg3_get_invariants(struct tg3 *tp)
|
|
|
*/
|
|
|
tg3_get_eeprom_hw_cfg(tp);
|
|
|
|
|
|
+ if (tp->tg3_flags3 & TG3_FLG3_ENABLE_APE) {
|
|
|
+ /* Allow reads and writes to the
|
|
|
+ * APE register and memory space.
|
|
|
+ */
|
|
|
+ pci_state_reg |= PCISTATE_ALLOW_APE_CTLSPC_WR |
|
|
|
+ PCISTATE_ALLOW_APE_SHMEM_WR;
|
|
|
+ pci_write_config_dword(tp->pdev, TG3PCI_PCISTATE,
|
|
|
+ pci_state_reg);
|
|
|
+ }
|
|
|
+
|
|
|
if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5784)
|
|
|
tp->tg3_flags |= TG3_FLAG_CPMU_PRESENT;
|
|
|
|
|
@@ -12165,13 +12357,35 @@ static int __devinit tg3_init_one(struct pci_dev *pdev,
|
|
|
|
|
|
tg3_init_coal(tp);
|
|
|
|
|
|
+ if (tp->tg3_flags3 & TG3_FLG3_ENABLE_APE) {
|
|
|
+ if (!(pci_resource_flags(pdev, 2) & IORESOURCE_MEM)) {
|
|
|
+ printk(KERN_ERR PFX "Cannot find proper PCI device "
|
|
|
+ "base address for APE, aborting.\n");
|
|
|
+ err = -ENODEV;
|
|
|
+ goto err_out_iounmap;
|
|
|
+ }
|
|
|
+
|
|
|
+ tg3reg_base = pci_resource_start(pdev, 2);
|
|
|
+ tg3reg_len = pci_resource_len(pdev, 2);
|
|
|
+
|
|
|
+ tp->aperegs = ioremap_nocache(tg3reg_base, tg3reg_len);
|
|
|
+ if (tp->aperegs == 0UL) {
|
|
|
+ printk(KERN_ERR PFX "Cannot map APE registers, "
|
|
|
+ "aborting.\n");
|
|
|
+ err = -ENOMEM;
|
|
|
+ goto err_out_iounmap;
|
|
|
+ }
|
|
|
+
|
|
|
+ tg3_ape_lock_init(tp);
|
|
|
+ }
|
|
|
+
|
|
|
pci_set_drvdata(pdev, dev);
|
|
|
|
|
|
err = register_netdev(dev);
|
|
|
if (err) {
|
|
|
printk(KERN_ERR PFX "Cannot register net device, "
|
|
|
"aborting.\n");
|
|
|
- goto err_out_iounmap;
|
|
|
+ goto err_out_apeunmap;
|
|
|
}
|
|
|
|
|
|
printk(KERN_INFO "%s: Tigon3 [partno(%s) rev %04x PHY(%s)] (%s) %s Ethernet ",
|
|
@@ -12204,6 +12418,12 @@ static int __devinit tg3_init_one(struct pci_dev *pdev,
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
+err_out_apeunmap:
|
|
|
+ if (tp->aperegs) {
|
|
|
+ iounmap(tp->aperegs);
|
|
|
+ tp->aperegs = NULL;
|
|
|
+ }
|
|
|
+
|
|
|
err_out_iounmap:
|
|
|
if (tp->regs) {
|
|
|
iounmap(tp->regs);
|
|
@@ -12231,6 +12451,10 @@ static void __devexit tg3_remove_one(struct pci_dev *pdev)
|
|
|
|
|
|
flush_scheduled_work();
|
|
|
unregister_netdev(dev);
|
|
|
+ if (tp->aperegs) {
|
|
|
+ iounmap(tp->aperegs);
|
|
|
+ tp->aperegs = NULL;
|
|
|
+ }
|
|
|
if (tp->regs) {
|
|
|
iounmap(tp->regs);
|
|
|
tp->regs = NULL;
|