]> git.saurik.com Git - apple/xnu.git/blob - iokit/Drivers/network/drvPPCUniN/UniNPowerSaver.cpp
xnu-124.13.tar.gz
[apple/xnu.git] / iokit / Drivers / network / drvPPCUniN / UniNPowerSaver.cpp
1
2 /*
3 * Copyright (c) 1999 Apple Computer, Inc. All rights reserved.
4 *
5 * @APPLE_LICENSE_HEADER_START@
6 *
7 * The contents of this file constitute Original Code as defined in and
8 * are subject to the Apple Public Source License Version 1.1 (the
9 * "License"). You may not use this file except in compliance with the
10 * License. Please obtain a copy of the License at
11 * http://www.apple.com/publicsource and read it before using this file.
12 *
13 * This Original Code and all software distributed under the License are
14 * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the
18 * License for the specific language governing rights and limitations
19 * under the License.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23
24
25 #include "UniNEnetPrivate.h"
26
27 #define super IOEthernetController
28
29 // Set EXTRANEOUS_PM_DELAYS to 1 to enable absurdly long delays.
30 //
31 #define EXTRANEOUS_PM_DELAYS 0
32
33 // --------------------------------------------------------------------------
34 // Method: registerWithPolicyMaker
35 //
36 // Purpose:
37 // initialize the driver for power managment and register ourselves with
38 // policy-maker
39 IOReturn
40 UniNEnet::registerWithPolicyMaker(IOService * policyMaker)
41 {
42
43 /******
44 From iokit/IOKit/pwr_mgt/IOPMpowerState.h
45 struct IOPMPowerState
46 {
47 unsigned long version; // version number of this struct
48 IOPMPowerFlags capabilityFlags; // bits that describe the capability
49 IOPMPowerFlags outputPowerCharacter; // description (to power domain children)
50 IOPMPowerFlags inputPowerRequirement; // description (to power domain parent)
51 unsigned long staticPower; // average consumption in milliwatts
52 unsigned long unbudgetedPower; // additional consumption from separate power supply (mw)
53 unsigned long powerToAttain; // additional power to attain this state from next lower state (in mw)
54 unsigned long timeToAttain; // (in microseconds)
55 unsigned long settleUpTime; // (microseconds)
56 unsigned long timeToLower; // (in microseconds)
57 unsigned long settleDownTime; // (microseconds)
58 unsigned long powerDomainBudget; // power in mw a domain in this state can deliver to its children
59 };
60
61 *******/
62
63 #define num_of_power_states 2
64
65 static IOPMPowerState ourPowerStates[num_of_power_states] = {
66 {1, 0,0,0,0,0,0,0,0,0,0,0},
67 {1,IOPMDeviceUsable | IOPMMaxPerformance, IOPMPowerOn, IOPMPowerOn, 50,0,0,
68 kUniNsettle_time, kUniNsettle_time, kUniNsettle_time, kUniNsettle_time,0}
69 // 50 milliwatts above is just a guess right now, since the ethernet is part of Uni-N
70 };
71
72 currentPowerState = kMaxUniNEnetPowerState;
73 return policyMaker->registerPowerDriver(this, ourPowerStates, num_of_power_states);
74 }
75
76 // Method: maxCapabilityForDomainState
77 //
78 // Purpose:
79 // returns the maximun state of card power, which would be
80 // power on without any attempt to power manager.
81 unsigned long
82 UniNEnet::maxCapabilityForDomainState(IOPMPowerFlags domainState)
83 {
84 if( domainState & IOPMPowerOn )
85 return kMaxUniNEnetPowerState; //In reality, it's just array element 1 for Uni-N
86 else
87 return 0;
88 }
89
90 // Method: initialPowerStateForDomainState
91 //
92 // Purpose:
93 // The power domain may be changing state. If power is on in the new
94 // state, that will not affect our state at all. If domain power is off,
95 // we can attain only our lowest state, which is off.
96
97 unsigned long
98 UniNEnet::initialPowerStateForDomainState( IOPMPowerFlags domainState )
99 {
100 if( domainState & IOPMPowerOn )
101 return currentPowerState;
102 else
103 return 0;
104 }
105
106
107 // Method: powerStateForDomainState
108 //
109 // Purpose:
110 // The power domain may be changing state. If power is on in the new
111 // state, that will not affect our state at all. If domain power is off,
112 // we can attain only our lowest state, which is off.
113 unsigned long
114 UniNEnet::powerStateForDomainState(IOPMPowerFlags domainState )
115 {
116 if( domainState & IOPMPowerOn )
117 return currentPowerState;
118 else
119 return 0;
120 }
121
122 // Method: setPowerState
123 //
124 IOReturn UniNEnet::setPowerState(unsigned long powerStateOrdinal,
125 IOService * whatDevice)
126 {
127 volatile UInt32 clockReg;
128
129 // Do not do anything if the state is invalid.
130 if (powerStateOrdinal >= num_of_power_states)
131 return IOPMNoSuchState;
132
133 if (powerStateOrdinal == currentPowerState)
134 return IOPMAckImplied; //no change required
135
136 // otherwise remember the new state:
137 currentPowerState = powerStateOrdinal;
138
139 IOLog("UniNEthernet::setPowerState(%d, 0x%08lx)\n",
140 (int) powerStateOrdinal,
141 (UInt32) whatDevice);
142
143 switch ( currentPowerState )
144 {
145 case 0: // Ethernet is off
146
147 // Shutdown the hardware unconditionally.
148 // doDisable(this);
149
150 // Turn off PHY before turning off MAC
151 // MII_CONTROL_POWERDOWN ? no, it is read-only for Broadcom 5201
152 // PHY, but 5400 is R/W
153 stopPHYChip(false); //In this file
154
155 // Now turn off ethernet clock in Uni-N
156 callPlatformFunction("EnableUniNEthernetClock", true,
157 (void *)false, 0, 0, 0);
158 break;
159
160 case kMaxUniNEnetPowerState: // 1 = max power state, Ethernet is on
161
162 // Now turn on ethernet clock in Uni-N
163 callPlatformFunction("EnableUniNEthernetClock", true,
164 (void *)true, 0, 0, 0);
165
166 #if EXTRANEOUS_PM_DELAYS
167 IODelay(MII_DEFAULT_DELAY * 1000); // 20 milliseconds
168 #endif
169
170 // Bring up PHY then MAC.
171 startPHYChip();
172 // doEnable(this);
173
174 break;
175
176 default:
177 // This is illegal, only 0 and 1 are allowed for
178 // UniN ethernet for now
179 break;
180 }
181
182 return IOPMAckImplied;
183 }
184
185 // This method sets up the PHY registers for low power.
186 // Copied from stopEthernetController() in OS9.
187 // The setupWOL value is not really implemented systemwide yet
188 void
189 UniNEnet::stopPHYChip(bool setupWOL)
190 {
191 UInt32 val32;
192 UInt16 i, val16;
193
194 if (phyBCMType == 0) return;
195
196 //IOLog("UniN on stop phy = %d\n", phyBCMType);
197
198 if (setupWOL == false)
199 {
200 //disabling MIF interrupts on the 5201 is explicit
201 if (phyBCMType == 5201)
202 {
203 miiWriteWord(0x0000, MII_BCM5201_INTERRUPT, kPHYAddr0);
204 // 0 or 0x1f or phyId? miiFindPHY returns any integer
205 }
206 }
207
208 //Drive the MDIO line high to prevent immediate wakeup
209 val32 = READ_REGISTER( MIFConfiguration );
210 WRITE_REGISTER( MIFConfiguration, val32 & kMIFConfiguration_Poll_Enable );
211
212 // 5th ADDR in Broadcom PHY docs
213 miiReadWord( &val16, MII_LINKPARTNER, kPHYAddr0 );
214
215 // don't know why OS9 writes it back unchanged
216 miiWriteWord( val16, MII_LINKPARTNER, kPHYAddr0 );
217
218 /* Put the MDIO pins into a benign state. Note that the management regs
219 in the PHY will be inaccessible. This is to guarantee max power savings
220 on Powerbooks and to eliminate damage to Broadcom PHYs.
221 */
222 //bit bang mode
223 WRITE_REGISTER( MIFConfiguration, kMIFConfiguration_BB_Mode );
224
225 WRITE_REGISTER( MIFBitBangClock, 0x0000 );
226 WRITE_REGISTER( MIFBitBangData, 0x0000 );
227 WRITE_REGISTER( MIFBitBangOutputEnable, 0x0000 );
228 WRITE_REGISTER( XIFConfiguration, kXIFConfiguration_GMIIMODE
229 | kXIFConfiguration_MII_Int_Loopback );
230
231 if (setupWOL)
232 {
233 //For multicast filtering these bits must be enabled
234 WRITE_REGISTER( RxMACConfiguration,
235 kRxMACConfiguration_Hash_Filter_Enable
236 | kRxMACConfiguration_Rx_Mac_Enable );
237 // set kpfRxMACEnabled in OS9, but I don't see matching OS X flag
238 }
239 else
240 {
241 WRITE_REGISTER( RxMACConfiguration, 0 );
242 // un-set kpfRxMACEnabled in OS9, but I don't see matching OS X flag
243 }
244
245 WRITE_REGISTER( TxMACConfiguration, 0 );
246 WRITE_REGISTER( XIFConfiguration, 0 );
247
248 #if 0
249 // Disable interrupt source on the controller.
250 // Already disabled from earlier resetAndEnable(false) call.
251 WRITE_REGISTER( InterruptMask, kInterruptMask_None ); // all FF
252 #endif
253
254 WRITE_REGISTER( TxConfiguration, 0 );
255 WRITE_REGISTER( RxConfiguration, 0 );
256
257 if (!setupWOL)
258 {
259 // this doesn't power down stuff, but if we don't hit it then we can't
260 // superisolate the transceiver
261 WRITE_REGISTER( SoftwareReset, kSoftwareReset_TX | kSoftwareReset_RX );
262
263 // kSoftwareReset_RSTOUT too???
264 i = 0;
265 do {
266 // IODelay(MII_RESET_DELAY * 1000); // 10 milliseconds
267 IODelay(10);
268 if (i++ >= 100)
269 {
270 IOLog("UniNEnet timeout on SW reset\n");
271 break;
272 }
273 val32 = READ_REGISTER( SoftwareReset );
274 } while ( (val32 & (kSoftwareReset_TX | kSoftwareReset_RX)) != 0 );
275
276 WRITE_REGISTER( TxMACSoftwareResetCommand, kTxMACSoftwareResetCommand_Reset );
277 WRITE_REGISTER( RxMACSoftwareResetCommand, kRxMACSoftwareResetCommand_Reset );
278
279 //This is what actually turns off the LINK LED
280 if (phyBCMType == 5400)
281 {
282 #if 0
283 // The 5400 has read/write privilege on this bit,
284 // but 5201 is read-only.
285 miiWriteWord( MII_CONTROL_POWERDOWN, MII_CONTROL, kPHYAddr0);
286 #endif
287 }
288 else // Only other possibility is Broadcom 5201 (or 5202?)
289 {
290 #if 0
291 miiReadWord( &val16, MII_BCM5201_AUXMODE2, kPHYAddr0 );
292 miiWriteWord( val16 & ~MII_BCM5201_AUXMODE2_LOWPOWER,
293 MII_BCM5201_AUXMODE2, kPHYAddr0 );
294 #endif
295
296 miiWriteWord( MII_BCM5201_MULTIPHY_SUPERISOLATE,
297 MII_BCM5201_MULTIPHY,
298 kPHYAddr0 );
299 }
300 } // end of none-WOL case
301 }
302
303 //start the PHY
304 void
305 UniNEnet::startPHYChip()
306 {
307 UInt32 val32;
308 UInt16 val16;
309
310 // if (netifClient) //MacOS 9 uses numClients == 1?
311 {
312 //IOLog("UniN on restart phy = %d\n", phyBCMType);
313
314 val32 = READ_REGISTER( TxConfiguration );
315 WRITE_REGISTER( TxConfiguration, val32 | kTxConfiguration_Tx_DMA_Enable );
316
317 val32 = READ_REGISTER( RxConfiguration );
318 WRITE_REGISTER( RxConfiguration, val32 | kRxConfiguration_Rx_DMA_Enable );
319
320 val32 = READ_REGISTER( TxMACConfiguration );
321 WRITE_REGISTER( TxMACConfiguration, val32 | kTxMACConfiguration_TxMac_Enable );
322
323 val32 = READ_REGISTER( RxMACConfiguration );
324 WRITE_REGISTER( RxMACConfiguration,
325 val32 | kRxMACConfiguration_Rx_Mac_Enable
326 | kRxMACConfiguration_Hash_Filter_Enable );
327
328 // Set flag to RxMACEnabled somewhere??
329
330 /* These registers are only for the Broadcom 5201.
331 We write the auto low power mode bit here because if we do it earlier
332 and there is no link then the xcvr registers become unclocked and
333 unable to be written
334 */
335 if (phyBCMType == 5201)
336 {
337 // Ask Enrique why the following 2 lines are not necessary in OS 9.
338 // These 2 lines should take the PHY out of superisolate mode. All
339 // MII inputs are ignored until the PHY is out of isolate mode
340 miiReadWord( &val16, MII_BCM5201_MULTIPHY, kPHYAddr0 );
341 miiWriteWord( val16 & ~MII_BCM5201_MULTIPHY_SUPERISOLATE,
342 MII_BCM5201_MULTIPHY, kPHYAddr0 );
343
344 #if 0
345 // Automatically go into low power mode if no link
346 miiReadWord( &val16, MII_BCM5201_AUXMODE2, kPHYAddr0 );
347 miiWriteWord( val16 | MII_BCM5201_AUXMODE2_LOWPOWER,
348 MII_BCM5201_AUXMODE2, kPHYAddr0 );
349 #endif
350
351 #if EXTRANEOUS_PM_DELAYS
352 IODelay(MII_DEFAULT_DELAY * 1000); // 20 milliseconds
353 #endif
354 }
355
356 // WARNING... this code is untested on gigabit ethernet (5400), there
357 // should be a case to handle it for MII_CONTROL_POWERDOWN bit here,
358 // unless it is unnecessary after a hardware reset
359
360 WRITE_REGISTER( RxKick, RX_RING_LENGTH - 4 );
361 }
362 }
363
364 /*-------------------------------------------------------------------------
365 * Assert the reset pin on the PHY momentarily to initialize it, and also
366 * to bring the PHY out of low-power mode.
367 *
368 *-------------------------------------------------------------------------*/
369
370 bool UniNEnet::resetPHYChip()
371 {
372 IOReturn result;
373
374 result = keyLargo->callPlatformFunction(keyLargo_resetUniNEthernetPhy, false, 0, 0, 0, 0);
375 if (result != kIOReturnSuccess) return false;
376
377 return true;
378 }