diff options
author | Stephen Hemminger <shemminger@vyatta.com> | 2008-05-14 20:04:17 -0400 |
---|---|---|
committer | Jeff Garzik <jgarzik@redhat.com> | 2008-05-30 22:19:17 -0400 |
commit | a068c0adf2fe28b324bca87f85d27af7f993cdaf (patch) | |
tree | 0c7685f8061825641d153abd738222ca05f52fac /drivers/net/sky2.c | |
parent | db99b98885e717454feef1c6868b27d3f23c2e7c (diff) |
sky2: pci power savings
Turn on special bits to save more power when device is shutdown.
Tested on a limited range of hardware, some of the bits are for hardware
that probably isn't even in production (like Yukon Supreme) and was ported
from the vendor driver.
Signed-off-by: Stephen Hemminger <shemminger@vyatta.com>
Signed-off-by: Jeff Garzik <jgarzik@redhat.com>
Diffstat (limited to 'drivers/net/sky2.c')
-rw-r--r-- | drivers/net/sky2.c | 98 |
1 files changed, 92 insertions, 6 deletions
diff --git a/drivers/net/sky2.c b/drivers/net/sky2.c index 59cb9cd00ad2..c83406f4f2a7 100644 --- a/drivers/net/sky2.c +++ b/drivers/net/sky2.c | |||
@@ -284,6 +284,86 @@ static void sky2_power_aux(struct sky2_hw *hw) | |||
284 | PC_VAUX_ON | PC_VCC_OFF)); | 284 | PC_VAUX_ON | PC_VCC_OFF)); |
285 | } | 285 | } |
286 | 286 | ||
287 | static void sky2_power_state(struct sky2_hw *hw, pci_power_t state) | ||
288 | { | ||
289 | u16 power_control = sky2_pci_read16(hw, hw->pm_cap + PCI_PM_CTRL); | ||
290 | int pex = pci_find_capability(hw->pdev, PCI_CAP_ID_EXP); | ||
291 | u32 reg; | ||
292 | |||
293 | sky2_write8(hw, B2_TST_CTRL1, TST_CFG_WRITE_ON); | ||
294 | |||
295 | switch (state) { | ||
296 | case PCI_D0: | ||
297 | break; | ||
298 | |||
299 | case PCI_D1: | ||
300 | power_control |= 1; | ||
301 | break; | ||
302 | |||
303 | case PCI_D2: | ||
304 | power_control |= 2; | ||
305 | break; | ||
306 | |||
307 | case PCI_D3hot: | ||
308 | case PCI_D3cold: | ||
309 | power_control |= 3; | ||
310 | if (hw->flags & SKY2_HW_ADV_POWER_CTL) { | ||
311 | /* additional power saving measurements */ | ||
312 | reg = sky2_pci_read32(hw, PCI_DEV_REG4); | ||
313 | |||
314 | /* set gating core clock for LTSSM in L1 state */ | ||
315 | reg |= P_PEX_LTSSM_STAT(P_PEX_LTSSM_L1_STAT) | | ||
316 | /* auto clock gated scheme controlled by CLKREQ */ | ||
317 | P_ASPM_A1_MODE_SELECT | | ||
318 | /* enable Gate Root Core Clock */ | ||
319 | P_CLK_GATE_ROOT_COR_ENA; | ||
320 | |||
321 | if (pex && (hw->flags & SKY2_HW_CLK_POWER)) { | ||
322 | /* enable Clock Power Management (CLKREQ) */ | ||
323 | u16 ctrl = sky2_pci_read16(hw, pex + PCI_EXP_DEVCTL); | ||
324 | |||
325 | ctrl |= PCI_EXP_DEVCTL_AUX_PME; | ||
326 | sky2_pci_write16(hw, pex + PCI_EXP_DEVCTL, ctrl); | ||
327 | } else | ||
328 | /* force CLKREQ Enable in Our4 (A1b only) */ | ||
329 | reg |= P_ASPM_FORCE_CLKREQ_ENA; | ||
330 | |||
331 | /* set Mask Register for Release/Gate Clock */ | ||
332 | sky2_pci_write32(hw, PCI_DEV_REG5, | ||
333 | P_REL_PCIE_EXIT_L1_ST | P_GAT_PCIE_ENTER_L1_ST | | ||
334 | P_REL_PCIE_RX_EX_IDLE | P_GAT_PCIE_RX_EL_IDLE | | ||
335 | P_REL_GPHY_LINK_UP | P_GAT_GPHY_LINK_DOWN); | ||
336 | } else | ||
337 | sky2_write8(hw, B28_Y2_ASF_STAT_CMD, Y2_ASF_CLK_HALT); | ||
338 | |||
339 | /* put CPU into reset state */ | ||
340 | sky2_write8(hw, B28_Y2_ASF_STAT_CMD, HCU_CCSR_ASF_RESET); | ||
341 | if (hw->chip_id == CHIP_ID_YUKON_SUPR && hw->chip_rev == CHIP_REV_YU_SU_A0) | ||
342 | /* put CPU into halt state */ | ||
343 | sky2_write8(hw, B28_Y2_ASF_STAT_CMD, HCU_CCSR_ASF_HALTED); | ||
344 | |||
345 | if (pex && !(hw->flags & SKY2_HW_RAM_BUFFER)) { | ||
346 | reg = sky2_pci_read32(hw, PCI_DEV_REG1); | ||
347 | /* force to PCIe L1 */ | ||
348 | reg |= PCI_FORCE_PEX_L1; | ||
349 | sky2_pci_write32(hw, PCI_DEV_REG1, reg); | ||
350 | } | ||
351 | break; | ||
352 | |||
353 | default: | ||
354 | dev_warn(&hw->pdev->dev, PFX "Invalid power state (%d) ", | ||
355 | state); | ||
356 | return; | ||
357 | } | ||
358 | |||
359 | power_control |= PCI_PM_CTRL_PME_ENABLE; | ||
360 | /* Finally, set the new power state. */ | ||
361 | sky2_pci_write32(hw, hw->pm_cap + PCI_PM_CTRL, power_control); | ||
362 | |||
363 | sky2_write8(hw, B2_TST_CTRL1, TST_CFG_WRITE_OFF); | ||
364 | sky2_pci_read32(hw, B0_CTST); | ||
365 | } | ||
366 | |||
287 | static void sky2_gmac_reset(struct sky2_hw *hw, unsigned port) | 367 | static void sky2_gmac_reset(struct sky2_hw *hw, unsigned port) |
288 | { | 368 | { |
289 | u16 reg; | 369 | u16 reg; |
@@ -2786,6 +2866,10 @@ static int __devinit sky2_init(struct sky2_hw *hw) | |||
2786 | hw->flags = SKY2_HW_GIGABIT | 2866 | hw->flags = SKY2_HW_GIGABIT |
2787 | | SKY2_HW_NEWER_PHY | 2867 | | SKY2_HW_NEWER_PHY |
2788 | | SKY2_HW_ADV_POWER_CTL; | 2868 | | SKY2_HW_ADV_POWER_CTL; |
2869 | |||
2870 | /* check for Rev. A1 dev 4200 */ | ||
2871 | if (sky2_read16(hw, Q_ADDR(Q_XA1, Q_WM)) == 0) | ||
2872 | hw->flags |= SKY2_HW_CLK_POWER; | ||
2789 | break; | 2873 | break; |
2790 | 2874 | ||
2791 | case CHIP_ID_YUKON_EX: | 2875 | case CHIP_ID_YUKON_EX: |
@@ -2836,6 +2920,11 @@ static int __devinit sky2_init(struct sky2_hw *hw) | |||
2836 | if (hw->pmd_type == 'L' || hw->pmd_type == 'S' || hw->pmd_type == 'P') | 2920 | if (hw->pmd_type == 'L' || hw->pmd_type == 'S' || hw->pmd_type == 'P') |
2837 | hw->flags |= SKY2_HW_FIBRE_PHY; | 2921 | hw->flags |= SKY2_HW_FIBRE_PHY; |
2838 | 2922 | ||
2923 | hw->pm_cap = pci_find_capability(hw->pdev, PCI_CAP_ID_PM); | ||
2924 | if (hw->pm_cap == 0) { | ||
2925 | dev_err(&hw->pdev->dev, "cannot find PowerManagement capability\n"); | ||
2926 | return -EIO; | ||
2927 | } | ||
2839 | 2928 | ||
2840 | hw->ports = 1; | 2929 | hw->ports = 1; |
2841 | t8 = sky2_read8(hw, B2_Y2_HW_RES); | 2930 | t8 = sky2_read8(hw, B2_Y2_HW_RES); |
@@ -4407,7 +4496,7 @@ static int sky2_suspend(struct pci_dev *pdev, pm_message_t state) | |||
4407 | 4496 | ||
4408 | pci_save_state(pdev); | 4497 | pci_save_state(pdev); |
4409 | pci_enable_wake(pdev, pci_choose_state(pdev, state), wol); | 4498 | pci_enable_wake(pdev, pci_choose_state(pdev, state), wol); |
4410 | pci_set_power_state(pdev, pci_choose_state(pdev, state)); | 4499 | sky2_power_state(hw, pci_choose_state(pdev, state)); |
4411 | 4500 | ||
4412 | return 0; | 4501 | return 0; |
4413 | } | 4502 | } |
@@ -4420,9 +4509,7 @@ static int sky2_resume(struct pci_dev *pdev) | |||
4420 | if (!hw) | 4509 | if (!hw) |
4421 | return 0; | 4510 | return 0; |
4422 | 4511 | ||
4423 | err = pci_set_power_state(pdev, PCI_D0); | 4512 | sky2_power_state(hw, PCI_D0); |
4424 | if (err) | ||
4425 | goto out; | ||
4426 | 4513 | ||
4427 | err = pci_restore_state(pdev); | 4514 | err = pci_restore_state(pdev); |
4428 | if (err) | 4515 | if (err) |
@@ -4490,8 +4577,7 @@ static void sky2_shutdown(struct pci_dev *pdev) | |||
4490 | pci_enable_wake(pdev, PCI_D3cold, wol); | 4577 | pci_enable_wake(pdev, PCI_D3cold, wol); |
4491 | 4578 | ||
4492 | pci_disable_device(pdev); | 4579 | pci_disable_device(pdev); |
4493 | pci_set_power_state(pdev, PCI_D3hot); | 4580 | sky2_power_state(hw, PCI_D3hot); |
4494 | |||
4495 | } | 4581 | } |
4496 | 4582 | ||
4497 | static struct pci_driver sky2_driver = { | 4583 | static struct pci_driver sky2_driver = { |