diff options
author | Stephen Hemminger <shemminger@linux-foundation.org> | 2007-02-02 11:22:53 -0500 |
---|---|---|
committer | Jeff Garzik <jeff@garzik.org> | 2007-02-06 19:07:44 -0500 |
commit | a504e64ab42bcc27074ea37405d06833ed6e0820 (patch) | |
tree | b7c9b772969bd72a2f3f1636943db18718fc41b9 /drivers/net | |
parent | 1479d13cb5304c452e6d7398c7771974c1014846 (diff) |
skge: WOL support
Add WOL support for Yukon chipsets in skge device.
Signed-off-by: Stephen Hemminger <shemminger@linux-foundation.org>
Signed-off-by: Jeff Garzik <jeff@garzik.org>
Diffstat (limited to 'drivers/net')
-rw-r--r-- | drivers/net/skge.c | 158 | ||||
-rw-r--r-- | drivers/net/skge.h | 2 |
2 files changed, 125 insertions, 35 deletions
diff --git a/drivers/net/skge.c b/drivers/net/skge.c index 9135602e157d..7e687ca4789a 100644 --- a/drivers/net/skge.c +++ b/drivers/net/skge.c | |||
@@ -132,18 +132,93 @@ static void skge_get_regs(struct net_device *dev, struct ethtool_regs *regs, | |||
132 | } | 132 | } |
133 | 133 | ||
134 | /* Wake on Lan only supported on Yukon chips with rev 1 or above */ | 134 | /* Wake on Lan only supported on Yukon chips with rev 1 or above */ |
135 | static int wol_supported(const struct skge_hw *hw) | 135 | static u32 wol_supported(const struct skge_hw *hw) |
136 | { | 136 | { |
137 | return !((hw->chip_id == CHIP_ID_GENESIS || | 137 | if (hw->chip_id == CHIP_ID_YUKON && hw->chip_rev != 0) |
138 | (hw->chip_id == CHIP_ID_YUKON && hw->chip_rev == 0))); | 138 | return WAKE_MAGIC | WAKE_PHY; |
139 | else | ||
140 | return 0; | ||
141 | } | ||
142 | |||
143 | static u32 pci_wake_enabled(struct pci_dev *dev) | ||
144 | { | ||
145 | int pm = pci_find_capability(dev, PCI_CAP_ID_PM); | ||
146 | u16 value; | ||
147 | |||
148 | /* If device doesn't support PM Capabilities, but request is to disable | ||
149 | * wake events, it's a nop; otherwise fail */ | ||
150 | if (!pm) | ||
151 | return 0; | ||
152 | |||
153 | pci_read_config_word(dev, pm + PCI_PM_PMC, &value); | ||
154 | |||
155 | value &= PCI_PM_CAP_PME_MASK; | ||
156 | value >>= ffs(PCI_PM_CAP_PME_MASK) - 1; /* First bit of mask */ | ||
157 | |||
158 | return value != 0; | ||
159 | } | ||
160 | |||
161 | static void skge_wol_init(struct skge_port *skge) | ||
162 | { | ||
163 | struct skge_hw *hw = skge->hw; | ||
164 | int port = skge->port; | ||
165 | enum pause_control save_mode; | ||
166 | u32 ctrl; | ||
167 | |||
168 | /* Bring hardware out of reset */ | ||
169 | skge_write16(hw, B0_CTST, CS_RST_CLR); | ||
170 | skge_write16(hw, SK_REG(port, GMAC_LINK_CTRL), GMLC_RST_CLR); | ||
171 | |||
172 | skge_write8(hw, SK_REG(port, GPHY_CTRL), GPC_RST_CLR); | ||
173 | skge_write8(hw, SK_REG(port, GMAC_CTRL), GMC_RST_CLR); | ||
174 | |||
175 | /* Force to 10/100 skge_reset will re-enable on resume */ | ||
176 | save_mode = skge->flow_control; | ||
177 | skge->flow_control = FLOW_MODE_SYMMETRIC; | ||
178 | |||
179 | ctrl = skge->advertising; | ||
180 | skge->advertising &= ~(ADVERTISED_1000baseT_Half|ADVERTISED_1000baseT_Full); | ||
181 | |||
182 | skge_phy_reset(skge); | ||
183 | |||
184 | skge->flow_control = save_mode; | ||
185 | skge->advertising = ctrl; | ||
186 | |||
187 | /* Set GMAC to no flow control and auto update for speed/duplex */ | ||
188 | gma_write16(hw, port, GM_GP_CTRL, | ||
189 | GM_GPCR_FC_TX_DIS|GM_GPCR_TX_ENA|GM_GPCR_RX_ENA| | ||
190 | GM_GPCR_DUP_FULL|GM_GPCR_FC_RX_DIS|GM_GPCR_AU_FCT_DIS); | ||
191 | |||
192 | /* Set WOL address */ | ||
193 | memcpy_toio(hw->regs + WOL_REGS(port, WOL_MAC_ADDR), | ||
194 | skge->netdev->dev_addr, ETH_ALEN); | ||
195 | |||
196 | /* Turn on appropriate WOL control bits */ | ||
197 | skge_write16(hw, WOL_REGS(port, WOL_CTRL_STAT), WOL_CTL_CLEAR_RESULT); | ||
198 | ctrl = 0; | ||
199 | if (skge->wol & WAKE_PHY) | ||
200 | ctrl |= WOL_CTL_ENA_PME_ON_LINK_CHG|WOL_CTL_ENA_LINK_CHG_UNIT; | ||
201 | else | ||
202 | ctrl |= WOL_CTL_DIS_PME_ON_LINK_CHG|WOL_CTL_DIS_LINK_CHG_UNIT; | ||
203 | |||
204 | if (skge->wol & WAKE_MAGIC) | ||
205 | ctrl |= WOL_CTL_ENA_PME_ON_MAGIC_PKT|WOL_CTL_ENA_MAGIC_PKT_UNIT; | ||
206 | else | ||
207 | ctrl |= WOL_CTL_DIS_PME_ON_MAGIC_PKT|WOL_CTL_DIS_MAGIC_PKT_UNIT;; | ||
208 | |||
209 | ctrl |= WOL_CTL_DIS_PME_ON_PATTERN|WOL_CTL_DIS_PATTERN_UNIT; | ||
210 | skge_write16(hw, WOL_REGS(port, WOL_CTRL_STAT), ctrl); | ||
211 | |||
212 | /* block receiver */ | ||
213 | skge_write8(hw, SK_REG(port, RX_GMF_CTRL_T), GMF_RST_SET); | ||
139 | } | 214 | } |
140 | 215 | ||
141 | static void skge_get_wol(struct net_device *dev, struct ethtool_wolinfo *wol) | 216 | static void skge_get_wol(struct net_device *dev, struct ethtool_wolinfo *wol) |
142 | { | 217 | { |
143 | struct skge_port *skge = netdev_priv(dev); | 218 | struct skge_port *skge = netdev_priv(dev); |
144 | 219 | ||
145 | wol->supported = wol_supported(skge->hw) ? WAKE_MAGIC : 0; | 220 | wol->supported = wol_supported(skge->hw); |
146 | wol->wolopts = skge->wol ? WAKE_MAGIC : 0; | 221 | wol->wolopts = skge->wol; |
147 | } | 222 | } |
148 | 223 | ||
149 | static int skge_set_wol(struct net_device *dev, struct ethtool_wolinfo *wol) | 224 | static int skge_set_wol(struct net_device *dev, struct ethtool_wolinfo *wol) |
@@ -151,23 +226,12 @@ static int skge_set_wol(struct net_device *dev, struct ethtool_wolinfo *wol) | |||
151 | struct skge_port *skge = netdev_priv(dev); | 226 | struct skge_port *skge = netdev_priv(dev); |
152 | struct skge_hw *hw = skge->hw; | 227 | struct skge_hw *hw = skge->hw; |
153 | 228 | ||
154 | if (wol->wolopts != WAKE_MAGIC && wol->wolopts != 0) | 229 | if (wol->wolopts & wol_supported(hw)) |
155 | return -EOPNOTSUPP; | ||
156 | |||
157 | if (wol->wolopts == WAKE_MAGIC && !wol_supported(hw)) | ||
158 | return -EOPNOTSUPP; | 230 | return -EOPNOTSUPP; |
159 | 231 | ||
160 | skge->wol = wol->wolopts == WAKE_MAGIC; | 232 | skge->wol = wol->wolopts; |
161 | 233 | if (!netif_running(dev)) | |
162 | if (skge->wol) { | 234 | skge_wol_init(skge); |
163 | memcpy_toio(hw->regs + WOL_MAC_ADDR, dev->dev_addr, ETH_ALEN); | ||
164 | |||
165 | skge_write16(hw, WOL_CTRL_STAT, | ||
166 | WOL_CTL_ENA_PME_ON_MAGIC_PKT | | ||
167 | WOL_CTL_ENA_MAGIC_PKT_UNIT); | ||
168 | } else | ||
169 | skge_write16(hw, WOL_CTRL_STAT, WOL_CTL_DEFAULT); | ||
170 | |||
171 | return 0; | 235 | return 0; |
172 | } | 236 | } |
173 | 237 | ||
@@ -3456,6 +3520,7 @@ static struct net_device *skge_devinit(struct skge_hw *hw, int port, | |||
3456 | skge->duplex = -1; | 3520 | skge->duplex = -1; |
3457 | skge->speed = -1; | 3521 | skge->speed = -1; |
3458 | skge->advertising = skge_supported_modes(hw); | 3522 | skge->advertising = skge_supported_modes(hw); |
3523 | skge->wol = pci_wake_enabled(hw->pdev) ? wol_supported(hw) : 0; | ||
3459 | 3524 | ||
3460 | hw->dev[port] = dev; | 3525 | hw->dev[port] = dev; |
3461 | 3526 | ||
@@ -3654,28 +3719,46 @@ static void __devexit skge_remove(struct pci_dev *pdev) | |||
3654 | } | 3719 | } |
3655 | 3720 | ||
3656 | #ifdef CONFIG_PM | 3721 | #ifdef CONFIG_PM |
3722 | static int vaux_avail(struct pci_dev *pdev) | ||
3723 | { | ||
3724 | int pm_cap; | ||
3725 | |||
3726 | pm_cap = pci_find_capability(pdev, PCI_CAP_ID_PM); | ||
3727 | if (pm_cap) { | ||
3728 | u16 ctl; | ||
3729 | pci_read_config_word(pdev, pm_cap + PCI_PM_PMC, &ctl); | ||
3730 | if (ctl & PCI_PM_CAP_AUX_POWER) | ||
3731 | return 1; | ||
3732 | } | ||
3733 | return 0; | ||
3734 | } | ||
3735 | |||
3736 | |||
3657 | static int skge_suspend(struct pci_dev *pdev, pm_message_t state) | 3737 | static int skge_suspend(struct pci_dev *pdev, pm_message_t state) |
3658 | { | 3738 | { |
3659 | struct skge_hw *hw = pci_get_drvdata(pdev); | 3739 | struct skge_hw *hw = pci_get_drvdata(pdev); |
3660 | int i, wol = 0; | 3740 | int i, err, wol = 0; |
3741 | |||
3742 | err = pci_save_state(pdev); | ||
3743 | if (err) | ||
3744 | return err; | ||
3661 | 3745 | ||
3662 | pci_save_state(pdev); | ||
3663 | for (i = 0; i < hw->ports; i++) { | 3746 | for (i = 0; i < hw->ports; i++) { |
3664 | struct net_device *dev = hw->dev[i]; | 3747 | struct net_device *dev = hw->dev[i]; |
3748 | struct skge_port *skge = netdev_priv(dev); | ||
3665 | 3749 | ||
3666 | if (netif_running(dev)) { | 3750 | if (netif_running(dev)) |
3667 | struct skge_port *skge = netdev_priv(dev); | 3751 | skge_down(dev); |
3752 | if (skge->wol) | ||
3753 | skge_wol_init(skge); | ||
3668 | 3754 | ||
3669 | netif_carrier_off(dev); | 3755 | wol |= skge->wol; |
3670 | if (skge->wol) | ||
3671 | netif_stop_queue(dev); | ||
3672 | else | ||
3673 | skge_down(dev); | ||
3674 | wol |= skge->wol; | ||
3675 | } | ||
3676 | netif_device_detach(dev); | ||
3677 | } | 3756 | } |
3678 | 3757 | ||
3758 | if (wol && vaux_avail(pdev)) | ||
3759 | skge_write8(hw, B0_POWER_CTRL, | ||
3760 | PC_VAUX_ENA | PC_VCC_ENA | PC_VAUX_ON | PC_VCC_OFF); | ||
3761 | |||
3679 | skge_write32(hw, B0_IMSK, 0); | 3762 | skge_write32(hw, B0_IMSK, 0); |
3680 | pci_enable_wake(pdev, pci_choose_state(pdev, state), wol); | 3763 | pci_enable_wake(pdev, pci_choose_state(pdev, state), wol); |
3681 | pci_set_power_state(pdev, pci_choose_state(pdev, state)); | 3764 | pci_set_power_state(pdev, pci_choose_state(pdev, state)); |
@@ -3688,8 +3771,14 @@ static int skge_resume(struct pci_dev *pdev) | |||
3688 | struct skge_hw *hw = pci_get_drvdata(pdev); | 3771 | struct skge_hw *hw = pci_get_drvdata(pdev); |
3689 | int i, err; | 3772 | int i, err; |
3690 | 3773 | ||
3691 | pci_set_power_state(pdev, PCI_D0); | 3774 | err = pci_set_power_state(pdev, PCI_D0); |
3692 | pci_restore_state(pdev); | 3775 | if (err) |
3776 | goto out; | ||
3777 | |||
3778 | err = pci_restore_state(pdev); | ||
3779 | if (err) | ||
3780 | goto out; | ||
3781 | |||
3693 | pci_enable_wake(pdev, PCI_D0, 0); | 3782 | pci_enable_wake(pdev, PCI_D0, 0); |
3694 | 3783 | ||
3695 | err = skge_reset(hw); | 3784 | err = skge_reset(hw); |
@@ -3699,7 +3788,6 @@ static int skge_resume(struct pci_dev *pdev) | |||
3699 | for (i = 0; i < hw->ports; i++) { | 3788 | for (i = 0; i < hw->ports; i++) { |
3700 | struct net_device *dev = hw->dev[i]; | 3789 | struct net_device *dev = hw->dev[i]; |
3701 | 3790 | ||
3702 | netif_device_attach(dev); | ||
3703 | if (netif_running(dev)) { | 3791 | if (netif_running(dev)) { |
3704 | err = skge_up(dev); | 3792 | err = skge_up(dev); |
3705 | 3793 | ||
diff --git a/drivers/net/skge.h b/drivers/net/skge.h index f6223c533c01..17b1b479dff5 100644 --- a/drivers/net/skge.h +++ b/drivers/net/skge.h | |||
@@ -876,11 +876,13 @@ enum { | |||
876 | WOL_PATT_CNT_0 = 0x0f38,/* 32 bit WOL Pattern Counter 3..0 */ | 876 | WOL_PATT_CNT_0 = 0x0f38,/* 32 bit WOL Pattern Counter 3..0 */ |
877 | WOL_PATT_CNT_4 = 0x0f3c,/* 24 bit WOL Pattern Counter 6..4 */ | 877 | WOL_PATT_CNT_4 = 0x0f3c,/* 24 bit WOL Pattern Counter 6..4 */ |
878 | }; | 878 | }; |
879 | #define WOL_REGS(port, x) (x + (port)*0x80) | ||
879 | 880 | ||
880 | enum { | 881 | enum { |
881 | WOL_PATT_RAM_1 = 0x1000,/* WOL Pattern RAM Link 1 */ | 882 | WOL_PATT_RAM_1 = 0x1000,/* WOL Pattern RAM Link 1 */ |
882 | WOL_PATT_RAM_2 = 0x1400,/* WOL Pattern RAM Link 2 */ | 883 | WOL_PATT_RAM_2 = 0x1400,/* WOL Pattern RAM Link 2 */ |
883 | }; | 884 | }; |
885 | #define WOL_PATT_RAM_BASE(port) (WOL_PATT_RAM_1 + (port)*0x400) | ||
884 | 886 | ||
885 | enum { | 887 | enum { |
886 | BASE_XMAC_1 = 0x2000,/* XMAC 1 registers */ | 888 | BASE_XMAC_1 = 0x2000,/* XMAC 1 registers */ |