diff options
author | shemminger@osdl.org <shemminger@osdl.org> | 2005-09-27 18:03:00 -0400 |
---|---|---|
committer | Jeff Garzik <jgarzik@pobox.com> | 2005-09-28 11:52:57 -0400 |
commit | 5afa0a9cfb79556f45c02957e71a2ac48a5bb6a1 (patch) | |
tree | 2043649c5061090b3603d3b48f861fddf7ca7255 /drivers/net/sky2.c | |
parent | d1f1370863f7fa3d76dc7d7779debdda854a5a60 (diff) |
[PATCH] sky2: explicit set power state
Add better power management, and power down the chip on device removal
Signed-off-by: Stephen Hemminger <shemminger@osdl.org>
Signed-off-by: Jeff Garzik <jgarzik@pobox.com>
Diffstat (limited to 'drivers/net/sky2.c')
-rw-r--r-- | drivers/net/sky2.c | 156 |
1 files changed, 111 insertions, 45 deletions
diff --git a/drivers/net/sky2.c b/drivers/net/sky2.c index 6d9c1db8896e..1d00cdcdaa67 100644 --- a/drivers/net/sky2.c +++ b/drivers/net/sky2.c | |||
@@ -175,6 +175,91 @@ ready: | |||
175 | return gma_read16(hw, port, GM_SMI_DATA); | 175 | return gma_read16(hw, port, GM_SMI_DATA); |
176 | } | 176 | } |
177 | 177 | ||
178 | static int sky2_set_power_state(struct sky2_hw *hw, pci_power_t state) | ||
179 | { | ||
180 | u16 power_control; | ||
181 | u32 reg1; | ||
182 | int vaux; | ||
183 | int ret = 0; | ||
184 | |||
185 | pr_debug("sky2_set_power_state %d\n", state); | ||
186 | sky2_write8(hw, B2_TST_CTRL1, TST_CFG_WRITE_ON); | ||
187 | |||
188 | pci_read_config_word(hw->pdev, hw->pm_cap + PCI_PM_PMC, &power_control); | ||
189 | vaux = (sky2_read8(hw, B0_CTST) & Y2_VAUX_AVAIL) && | ||
190 | (power_control & PCI_PM_CAP_PME_D3cold); | ||
191 | |||
192 | pci_read_config_word(hw->pdev, hw->pm_cap + PCI_PM_CTRL, &power_control); | ||
193 | |||
194 | power_control |= PCI_PM_CTRL_PME_STATUS; | ||
195 | power_control &= ~(PCI_PM_CTRL_STATE_MASK); | ||
196 | |||
197 | switch (state) { | ||
198 | case PCI_D0: | ||
199 | /* switch power to VCC (WA for VAUX problem) */ | ||
200 | sky2_write8(hw, B0_POWER_CTRL, | ||
201 | PC_VAUX_ENA | PC_VCC_ENA | PC_VAUX_OFF | PC_VCC_ON); | ||
202 | |||
203 | /* disable Core Clock Division, */ | ||
204 | sky2_write32(hw, B2_Y2_CLK_CTRL, Y2_CLK_DIV_DIS); | ||
205 | |||
206 | if (hw->chip_id == CHIP_ID_YUKON_XL && hw->chip_rev > 1) | ||
207 | /* enable bits are inverted */ | ||
208 | sky2_write8(hw, B2_Y2_CLK_GATE, | ||
209 | Y2_PCI_CLK_LNK1_DIS | Y2_COR_CLK_LNK1_DIS | | ||
210 | Y2_CLK_GAT_LNK1_DIS | Y2_PCI_CLK_LNK2_DIS | | ||
211 | Y2_COR_CLK_LNK2_DIS | Y2_CLK_GAT_LNK2_DIS); | ||
212 | else | ||
213 | sky2_write8(hw, B2_Y2_CLK_GATE, 0); | ||
214 | |||
215 | /* Turn off phy power saving */ | ||
216 | pci_read_config_dword(hw->pdev, PCI_DEV_REG1, ®1); | ||
217 | reg1 &= ~(PCI_Y2_PHY1_POWD | PCI_Y2_PHY2_POWD); | ||
218 | |||
219 | /* looks like this xl is back asswards .. */ | ||
220 | if (hw->chip_id == CHIP_ID_YUKON_XL && hw->chip_rev > 1) { | ||
221 | reg1 |= PCI_Y2_PHY1_COMA; | ||
222 | if (hw->ports > 1) | ||
223 | reg1 |= PCI_Y2_PHY2_COMA; | ||
224 | } | ||
225 | pci_write_config_dword(hw->pdev, PCI_DEV_REG1, reg1); | ||
226 | break; | ||
227 | |||
228 | case PCI_D3hot: | ||
229 | case PCI_D3cold: | ||
230 | /* Turn on phy power saving */ | ||
231 | pci_read_config_dword(hw->pdev, PCI_DEV_REG1, ®1); | ||
232 | if (hw->chip_id == CHIP_ID_YUKON_XL && hw->chip_rev > 1) | ||
233 | reg1 &= ~(PCI_Y2_PHY1_POWD | PCI_Y2_PHY2_POWD); | ||
234 | else | ||
235 | reg1 |= (PCI_Y2_PHY1_POWD | PCI_Y2_PHY2_POWD); | ||
236 | pci_write_config_dword(hw->pdev, PCI_DEV_REG1, reg1); | ||
237 | |||
238 | if (hw->chip_id == CHIP_ID_YUKON_XL && hw->chip_rev > 1) | ||
239 | sky2_write8(hw, B2_Y2_CLK_GATE, 0); | ||
240 | else | ||
241 | /* enable bits are inverted */ | ||
242 | sky2_write8(hw, B2_Y2_CLK_GATE, | ||
243 | Y2_PCI_CLK_LNK1_DIS | Y2_COR_CLK_LNK1_DIS | | ||
244 | Y2_CLK_GAT_LNK1_DIS | Y2_PCI_CLK_LNK2_DIS | | ||
245 | Y2_COR_CLK_LNK2_DIS | Y2_CLK_GAT_LNK2_DIS); | ||
246 | |||
247 | /* switch power to VAUX */ | ||
248 | if (vaux && state != PCI_D3cold) | ||
249 | sky2_write8(hw, B0_POWER_CTRL, | ||
250 | (PC_VAUX_ENA | PC_VCC_ENA | | ||
251 | PC_VAUX_ON | PC_VCC_OFF)); | ||
252 | break; | ||
253 | default: | ||
254 | printk(KERN_ERR PFX "Unknown power state %d\n", state); | ||
255 | ret = -1; | ||
256 | } | ||
257 | |||
258 | pci_write_config_byte(hw->pdev, hw->pm_cap + PCI_PM_CTRL, power_control); | ||
259 | sky2_write8(hw, B2_TST_CTRL1, TST_CFG_WRITE_OFF); | ||
260 | return ret; | ||
261 | } | ||
262 | |||
178 | static void sky2_phy_reset(struct sky2_hw *hw, unsigned port) | 263 | static void sky2_phy_reset(struct sky2_hw *hw, unsigned port) |
179 | { | 264 | { |
180 | u16 reg; | 265 | u16 reg; |
@@ -1869,7 +1954,7 @@ static inline u32 sky2_us2clk(const struct sky2_hw *hw, u32 us) | |||
1869 | 1954 | ||
1870 | static int sky2_reset(struct sky2_hw *hw) | 1955 | static int sky2_reset(struct sky2_hw *hw) |
1871 | { | 1956 | { |
1872 | u32 ctst, power; | 1957 | u32 ctst; |
1873 | u16 status; | 1958 | u16 status; |
1874 | u8 t8, pmd_type; | 1959 | u8 t8, pmd_type; |
1875 | int i; | 1960 | int i; |
@@ -1927,33 +2012,7 @@ static int sky2_reset(struct sky2_hw *hw) | |||
1927 | } | 2012 | } |
1928 | hw->chip_rev = (sky2_read8(hw, B2_MAC_CFG) & CFG_CHIP_R_MSK) >> 4; | 2013 | hw->chip_rev = (sky2_read8(hw, B2_MAC_CFG) & CFG_CHIP_R_MSK) >> 4; |
1929 | 2014 | ||
1930 | /* switch power to VCC (WA for VAUX problem) */ | 2015 | sky2_set_power_state(hw, PCI_D0); |
1931 | sky2_write8(hw, B0_POWER_CTRL, | ||
1932 | PC_VAUX_ENA | PC_VCC_ENA | PC_VAUX_OFF | PC_VCC_ON); | ||
1933 | |||
1934 | /* disable Core Clock Division, */ | ||
1935 | sky2_write32(hw, B2_Y2_CLK_CTRL, Y2_CLK_DIV_DIS); | ||
1936 | |||
1937 | if (hw->chip_id == CHIP_ID_YUKON_XL && hw->chip_rev > 1) | ||
1938 | /* enable bits are inverted */ | ||
1939 | sky2_write8(hw, B2_Y2_CLK_GATE, | ||
1940 | Y2_PCI_CLK_LNK1_DIS | Y2_COR_CLK_LNK1_DIS | | ||
1941 | Y2_CLK_GAT_LNK1_DIS | Y2_PCI_CLK_LNK2_DIS | | ||
1942 | Y2_COR_CLK_LNK2_DIS | Y2_CLK_GAT_LNK2_DIS); | ||
1943 | else | ||
1944 | sky2_write8(hw, B2_Y2_CLK_GATE, 0); | ||
1945 | |||
1946 | /* Turn off phy power saving */ | ||
1947 | pci_read_config_dword(hw->pdev, PCI_DEV_REG1, &power); | ||
1948 | power &= ~(PCI_Y2_PHY1_POWD | PCI_Y2_PHY2_POWD); | ||
1949 | |||
1950 | /* looks like this xl is back asswards .. */ | ||
1951 | if (hw->chip_id == CHIP_ID_YUKON_XL && hw->chip_rev > 1) { | ||
1952 | power |= PCI_Y2_PHY1_COMA; | ||
1953 | if (hw->ports > 1) | ||
1954 | power |= PCI_Y2_PHY2_COMA; | ||
1955 | } | ||
1956 | pci_write_config_dword(hw->pdev, PCI_DEV_REG1, power); | ||
1957 | 2016 | ||
1958 | for (i = 0; i < hw->ports; i++) { | 2017 | for (i = 0; i < hw->ports; i++) { |
1959 | sky2_write8(hw, SK_REG(i, GMAC_LINK_CTRL), GMLC_RST_SET); | 2018 | sky2_write8(hw, SK_REG(i, GMAC_LINK_CTRL), GMLC_RST_SET); |
@@ -2714,7 +2773,7 @@ static int __devinit sky2_probe(struct pci_dev *pdev, | |||
2714 | { | 2773 | { |
2715 | struct net_device *dev, *dev1 = NULL; | 2774 | struct net_device *dev, *dev1 = NULL; |
2716 | struct sky2_hw *hw; | 2775 | struct sky2_hw *hw; |
2717 | int err, using_dac = 0; | 2776 | int err, pm_cap, using_dac = 0; |
2718 | 2777 | ||
2719 | err = pci_enable_device(pdev); | 2778 | err = pci_enable_device(pdev); |
2720 | if (err) { | 2779 | if (err) { |
@@ -2732,6 +2791,15 @@ static int __devinit sky2_probe(struct pci_dev *pdev, | |||
2732 | 2791 | ||
2733 | pci_set_master(pdev); | 2792 | pci_set_master(pdev); |
2734 | 2793 | ||
2794 | /* Find power-management capability. */ | ||
2795 | pm_cap = pci_find_capability(pdev, PCI_CAP_ID_PM); | ||
2796 | if (pm_cap == 0) { | ||
2797 | printk(KERN_ERR PFX "Cannot find PowerManagement capability, " | ||
2798 | "aborting.\n"); | ||
2799 | err = -EIO; | ||
2800 | goto err_out_free_regions; | ||
2801 | } | ||
2802 | |||
2735 | if (sizeof(dma_addr_t) > sizeof(u32)) { | 2803 | if (sizeof(dma_addr_t) > sizeof(u32)) { |
2736 | err = pci_set_dma_mask(pdev, DMA_64BIT_MASK); | 2804 | err = pci_set_dma_mask(pdev, DMA_64BIT_MASK); |
2737 | if (!err) | 2805 | if (!err) |
@@ -2775,6 +2843,7 @@ static int __devinit sky2_probe(struct pci_dev *pdev, | |||
2775 | pci_name(pdev)); | 2843 | pci_name(pdev)); |
2776 | goto err_out_free_hw; | 2844 | goto err_out_free_hw; |
2777 | } | 2845 | } |
2846 | hw->pm_cap = pm_cap; | ||
2778 | 2847 | ||
2779 | err = sky2_reset(hw); | 2848 | err = sky2_reset(hw); |
2780 | if (err) | 2849 | if (err) |
@@ -2861,8 +2930,10 @@ static void __devexit sky2_remove(struct pci_dev *pdev) | |||
2861 | unregister_netdev(dev0); | 2930 | unregister_netdev(dev0); |
2862 | 2931 | ||
2863 | sky2_write32(hw, B0_IMSK, 0); | 2932 | sky2_write32(hw, B0_IMSK, 0); |
2933 | sky2_set_power_state(hw, PCI_D3hot); | ||
2864 | sky2_write16(hw, B0_Y2LED, LED_STAT_OFF); | 2934 | sky2_write16(hw, B0_Y2LED, LED_STAT_OFF); |
2865 | sky2_write8(hw, B0_CTST, CS_RST_SET); | 2935 | sky2_write8(hw, B0_CTST, CS_RST_SET); |
2936 | sky2_read8(hw, B0_CTST); | ||
2866 | 2937 | ||
2867 | free_irq(pdev->irq, hw); | 2938 | free_irq(pdev->irq, hw); |
2868 | pci_free_consistent(pdev, STATUS_LE_BYTES, hw->st_le, hw->st_dma); | 2939 | pci_free_consistent(pdev, STATUS_LE_BYTES, hw->st_le, hw->st_dma); |
@@ -2874,6 +2945,7 @@ static void __devexit sky2_remove(struct pci_dev *pdev) | |||
2874 | free_netdev(dev0); | 2945 | free_netdev(dev0); |
2875 | iounmap(hw->regs); | 2946 | iounmap(hw->regs); |
2876 | kfree(hw); | 2947 | kfree(hw); |
2948 | |||
2877 | pci_set_drvdata(pdev, NULL); | 2949 | pci_set_drvdata(pdev, NULL); |
2878 | } | 2950 | } |
2879 | 2951 | ||
@@ -2881,28 +2953,21 @@ static void __devexit sky2_remove(struct pci_dev *pdev) | |||
2881 | static int sky2_suspend(struct pci_dev *pdev, pm_message_t state) | 2953 | static int sky2_suspend(struct pci_dev *pdev, pm_message_t state) |
2882 | { | 2954 | { |
2883 | struct sky2_hw *hw = pci_get_drvdata(pdev); | 2955 | struct sky2_hw *hw = pci_get_drvdata(pdev); |
2884 | int i, wol = 0; | 2956 | int i; |
2885 | 2957 | ||
2886 | for (i = 0; i < 2; i++) { | 2958 | for (i = 0; i < 2; i++) { |
2887 | struct net_device *dev = hw->dev[i]; | 2959 | struct net_device *dev = hw->dev[i]; |
2888 | 2960 | ||
2889 | if (dev) { | 2961 | if (dev) { |
2890 | struct sky2_port *sky2 = netdev_priv(dev); | 2962 | if (!netif_running(dev)) |
2891 | if (netif_running(dev)) { | 2963 | continue; |
2892 | netif_carrier_off(dev); | 2964 | |
2893 | sky2_down(dev); | 2965 | sky2_down(dev); |
2894 | } | ||
2895 | netif_device_detach(dev); | 2966 | netif_device_detach(dev); |
2896 | wol |= sky2->wol; | ||
2897 | } | 2967 | } |
2898 | } | 2968 | } |
2899 | 2969 | ||
2900 | pci_save_state(pdev); | 2970 | return sky2_set_power_state(hw, pci_choose_state(pdev, state)); |
2901 | pci_enable_wake(pdev, pci_choose_state(pdev, state), wol); | ||
2902 | pci_disable_device(pdev); | ||
2903 | pci_set_power_state(pdev, pci_choose_state(pdev, state)); | ||
2904 | |||
2905 | return 0; | ||
2906 | } | 2971 | } |
2907 | 2972 | ||
2908 | static int sky2_resume(struct pci_dev *pdev) | 2973 | static int sky2_resume(struct pci_dev *pdev) |
@@ -2910,18 +2975,19 @@ static int sky2_resume(struct pci_dev *pdev) | |||
2910 | struct sky2_hw *hw = pci_get_drvdata(pdev); | 2975 | struct sky2_hw *hw = pci_get_drvdata(pdev); |
2911 | int i; | 2976 | int i; |
2912 | 2977 | ||
2913 | pci_set_power_state(pdev, PCI_D0); | ||
2914 | pci_restore_state(pdev); | 2978 | pci_restore_state(pdev); |
2915 | pci_enable_wake(pdev, PCI_D0, 0); | 2979 | pci_enable_wake(pdev, PCI_D0, 0); |
2980 | sky2_set_power_state(hw, PCI_D0); | ||
2916 | 2981 | ||
2917 | sky2_reset(hw); | 2982 | sky2_reset(hw); |
2918 | 2983 | ||
2919 | for (i = 0; i < 2; i++) { | 2984 | for (i = 0; i < 2; i++) { |
2920 | struct net_device *dev = hw->dev[i]; | 2985 | struct net_device *dev = hw->dev[i]; |
2921 | if (dev) { | 2986 | if (dev) { |
2922 | netif_device_attach(dev); | 2987 | if (netif_running(dev)) { |
2923 | if (netif_running(dev)) | 2988 | netif_device_attach(dev); |
2924 | sky2_up(dev); | 2989 | sky2_up(dev); |
2990 | } | ||
2925 | } | 2991 | } |
2926 | } | 2992 | } |
2927 | return 0; | 2993 | return 0; |