1/*
2 * Copyright (c) 2005-2006 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. The rights granted to you under the License
10 * may not be used to create, or enable the creation or redistribution of,
11 * unlawful or unlicensed copies of an Apple operating system, or to
12 * circumvent, violate, or enable the circumvention or violation of, any
13 * terms of an Apple operating system software license agreement.
14 *
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
17 *
18 * The Original Code and all software distributed under the License are
19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23 * Please see the License for the specific language governing rights and
24 * limitations under the License.
25 *
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
27 */
28#include <machine/machine_routines.h>
29#include <machine/machine_cpu.h>
30#ifdef __ppc__
31# include <ppc/exception.h>
32# include <ppc/misc_protos.h>
33# include <ppc/cpu_internal.h>
34#else
35# include <i386/cpu_data.h>
36# include <i386/misc_protos.h>
37#endif
38#include <machine/pmap.h>
39#include <kern/pms.h>
40#include <kern/processor.h>
41#include <kern/kalloc.h>
42#include <vm/vm_protos.h>
43
44extern int is_suser(void);
45
46static uint32_t pmsSyncrolator = 0;					/* Only one control operation at a time please */
47uint32_t pmsBroadcastWait = 0;						/* Number of outstanding broadcasts */
48
49int pmsInstalled = 0;								/* Power Management Stepper can run and has table installed */
50int pmsExperimental = 0;							/* Power Management Stepper in experimental mode */
51decl_simple_lock_data(,pmsBuildLock)				/* Make sure only one guy can replace table  at the same time */
52
53static pmsDef *altDpmsTab;						/* Alternate step definition table */
54static uint32_t altDpmsTabSize = 0;					/* Size of alternate step definition table */
55
56pmsDef pmsDummy = {									/* This is the dummy step for initialization.  All it does is to park */
57	.pmsLimit = 0,									/* Time doesn't matter for a park */
58	.pmsStepID = pmsMaxStates - 1,					/* Use the very last ID number for the dummy */
59	.pmsSetCmd = pmsParkIt,							/* Force us to be parked */
60	.sf.pmsSetFuncInd = 0,							/* No platform call for this one */
61	.pmsDown = pmsPrepSleep,						/* We always park */
62	.pmsNext = pmsPrepSleep							/* We always park */
63};
64
65pmsStat pmsStatsd[4][pmsMaxStates];					/* Generate enough statistics blocks for 4 processors */
66
67pmsCtl pmsCtls = {									/* Power Management Stepper control */
68	.pmsStats = pmsStatsd,
69};
70
71pmsSetFunc_t pmsFuncTab[pmsSetFuncMax] = {NULL};		/* This is the function index table */
72pmsQueryFunc_t pmsQueryFunc;					/* Pointer to pmsQuery function */
73uint32_t pmsPlatformData = 0;						/* Data provided by and passed to platform functions */
74
75#ifdef __ppc__
76# define PER_PROC_INFO		struct per_proc_info
77# define GET_PER_PROC_INFO()	getPerProc()
78#else
79# define PER_PROC_INFO 		cpu_data_t
80# define GET_PER_PROC_INFO()	current_cpu_datap()
81#endif
82
83
84
85/*
86 *	Do any initialization needed
87 */
88
89void
90pmsInit(void)
91{
92	int i;
93
94	simple_lock_init(&pmsBuildLock, 0);				/* Initialize the build lock */
95	for(i = 0; i < pmsMaxStates; i++) pmsCtls.pmsDefs[i] = &pmsDummy;	/* Initialize the table to dummy steps */
96
97	pmsCPUMachineInit();
98}
99
100
101/*
102 *	Start the power management stepper on all processors
103 *
104 *	All processors must be parked.  This should be called when the hardware
105 *	is ready to step.  Probably only at boot and after wake from sleep.
106 *
107 */
108
109 void
110 pmsStart(void)
111{
112 	boolean_t	intr;
113
114	if(!pmsInstalled)
115		return;						/* We can't do this if no table installed */
116
117	intr = ml_set_interrupts_enabled(FALSE);		/* No interruptions in here */
118	pmsRun(pmsStartUp);								/* Start running the stepper everywhere */
119	(void)ml_set_interrupts_enabled(intr);			/* Restore interruptions */
120 }
121
122
123/*
124 *	Park the stepper execution.  This will force the stepper on this
125 *	processor to abandon its current step and stop.  No changes to the
126 *	hardware state is made and any previous step is lost.
127 *
128 *	This is used as the initial state at startup and when the step table
129 *	is being changed.
130 *
131 */
132
133void
134pmsPark(void)
135{
136	boolean_t	intr;
137
138	if(!pmsInstalled)
139		return;						/* We can't do this if no table installed */
140
141	intr = ml_set_interrupts_enabled(FALSE);		/* No interruptions in here */
142	pmsSetStep(pmsParked, 0);						/* Park the stepper */
143	(void)ml_set_interrupts_enabled(intr);			/* Restore interruptions */
144}
145
146
147/*
148 *	Steps down to a lower power.
149 *	Interrupts must be off...
150 */
151
152void
153pmsDown(void)
154{
155	PER_PROC_INFO *pp;
156	uint32_t nstate;
157
158	pp = GET_PER_PROC_INFO();								/* Get our per_proc */
159
160	if(!pmsInstalled || pp->pms.pmsState == pmsParked)
161		return;		/* No stepping if parked or not installed */
162
163	nstate = pmsCtls.pmsDefs[pp->pms.pmsState]->pmsDown;	/* Get the downward step */
164	pmsSetStep(nstate, 0);							/* Step to it */
165}
166
167
168/*
169 *	Steps up to a higher power.  The "timer" parameter is true if the
170 *	step was driven due to the pms timer expiring.
171 *
172 *	Interrupts must be off...
173 */
174
175int pmsStepIdleSneaks;
176int pmsStepIdleTries;
177
178void
179pmsStep(int timer)
180{
181	PER_PROC_INFO	*pp;
182	uint32_t	nstate;
183	uint32_t	tstate;
184	uint32_t	pkgstate;
185	int		dir;
186	int		i;
187
188	pp = GET_PER_PROC_INFO();								/* Get our per_proc */
189
190	if(!pmsInstalled || pp->pms.pmsState == pmsParked)
191		return;	/* No stepping if parked or not installed */
192
193	/*
194	 * Assume a normal step.
195	 */
196	nstate = pmsCtls.pmsDefs[pp->pms.pmsState]->pmsNext;
197
198	/*
199	 * If we are idling and being asked to step up, check to see whether
200	 * the package we're in is already at a non-idle power state.  If so,
201	 * attempt to work out what state that is, and go there directly to
202	 * avoid wasting time ramping up.
203	 */
204	if ((pp->pms.pmsState == pmsIdle)
205	    && ((pkgstate = pmsCPUPackageQuery()) != ~(uint32_t)0)) {
206		/*
207		 * Search forward through the stepper program,
208		 * avoid looping for too long.
209		 */
210		tstate = nstate;
211		pmsStepIdleTries++;
212		for (i = 0; i < 32; i++) {
213		    /*
214		     * Compare command with current package state
215		     */
216		    if ((pmsCtls.pmsDefs[tstate]->pmsSetCmd & pmsCPU) == pkgstate) {
217			nstate = tstate;
218			pmsStepIdleSneaks++;
219			break;
220		    }
221
222		    /*
223		     * Advance to the next step in the program.
224		     */
225		    if (pmsCtls.pmsDefs[tstate]->pmsNext == tstate)
226			break;	/* infinite loop */
227		    tstate = pmsCtls.pmsDefs[tstate]->pmsNext;
228		}
229        }
230
231	/*
232	 * Default to a step up.
233	 */
234	dir = 1;
235
236	/*
237	 * If we are stepping as a consequence of timer expiry, select the
238	 * alternate exit path and note this as downward step for accounting
239	 * purposes.
240	 */
241	if (timer
242	    && (pmsCtls.pmsDefs[pp->pms.pmsState]->pmsSetCmd == pmsDelay)) {
243	    nstate = pmsCtls.pmsDefs[pp->pms.pmsState]->pmsTDelay;
244
245	    /*
246	     * Delayed steps are a step down for accounting purposes.
247	     */
248	    dir = 0;
249	}
250
251	pmsSetStep(nstate, dir);
252}
253
254
255/*
256 *	Set a specific step
257 *
258 *	We do not do statistics if exiting park
259 *	Interrupts must be off...
260 *
261 */
262
263void
264pmsSetStep(uint32_t nstep, int dir)
265{
266	PER_PROC_INFO *pp;
267	uint32_t pstate, nCSetCmd, mCSetCmd;
268	pmsDef *pnstate, *pcstate;
269	uint64_t tb, dur;
270	int cpu;
271
272	pp = GET_PER_PROC_INFO();								/* Get our per_proc */
273	cpu = cpu_number();								/* Get our processor */
274
275	while(1) {										/* Keep stepping until we get a delay */
276
277		if(pp->pms.pmsCSetCmd & pmsMustCmp) {		/* Do we have to finish the delay before changing? */
278			while(mach_absolute_time() < pp->pms.pmsPop);	/* Yes, spin here... */
279		}
280
281		if((nstep == pmsParked) || ((uint32_t)pmsCtls.pmsDefs[nstep]->pmsSetCmd == pmsParkIt)) {	/* Are we parking? */
282
283			tb = mach_absolute_time();				/* What time is it? */
284			pp->pms.pmsStamp = tb;					/* Show transition now */
285			pp->pms.pmsPop = HalfwayToForever;		/* Set the pop way into the future */
286			pp->pms.pmsState = pmsParked;			/* Make sure we are parked */
287			etimer_resync_deadlines();							/* Cancel our timer if going */
288			return;
289		}
290
291		pnstate = pmsCtls.pmsDefs[nstep];			/* Point to the state definition */
292		pstate = pp->pms.pmsState;					/* Save the current step */
293		pp->pms.pmsState = nstep;					/* Set the current to the next step */
294
295		if(pnstate->pmsSetCmd != pmsDelay) {		/* If this is not a delayed state, change the actual hardware now */
296			if(pnstate->pmsSetCmd & pmsCngCPU) pmsCPUSet(pnstate->pmsSetCmd);	/* We have some CPU work to do... */
297			if((uint32_t)pnstate->sf.pmsSetFunc) pnstate->sf.pmsSetFunc(pnstate->pmsSetCmd, cpu, pmsPlatformData);	/* Tell the platform to set power mode */
298
299			mCSetCmd = pnstate->pmsSetCmd & (pmsCngXClk | pmsCngCPU | pmsCngVolt);	/* Isolate just the change flags */
300			mCSetCmd = (mCSetCmd - (mCSetCmd >> 7)) | pmsSync | pmsMustCmp | pmsPowerID;	/* Form mask of bits that come from new command */
301			nCSetCmd = pp->pms.pmsCSetCmd & ~mCSetCmd;	/* Clear changing bits */
302			nCSetCmd = nCSetCmd | (pnstate->pmsSetCmd & mCSetCmd);	/* Flip on the changing bits and the always copy bits */
303
304			pp->pms.pmsCSetCmd = nCSetCmd;			/* Set it for real */
305		}
306
307		tb = mach_absolute_time();					/* What time is it? */
308		pp->pms.pmsPop = tb + pnstate->pmsLimit;	/* Set the next pop */
309
310		if((pnstate->pmsSetCmd != pmsDelay) && (pp->pms.pmsCSetCmd & pmsSync) && (pnstate->pmsLimit != 0)) {	/* Is this a synchronous command with a delay? */
311			while(mach_absolute_time() < pp->pms.pmsPop);	/* Yes, spin here and wait it out... */
312		}
313
314/*
315 *		Gather some statistics
316 */
317
318		dur = tb - pp->pms.pmsStamp;				/* Get the amount of time we were in the old step */
319		pp->pms.pmsStamp = tb;						/* Set the new timestamp */
320		if(!(pstate == pmsParked)) {				/* Only take stats if we were not parked */
321			pcstate = pmsCtls.pmsDefs[pstate];		/* Get the previous step */
322			pmsCtls.pmsStats[cpu][pcstate->pmsStepID].stTime[dir] += dur;	/* Accumulate the total time in the old step */
323			pmsCtls.pmsStats[cpu][pcstate->pmsStepID].stCnt[dir] += 1;	/* Count transitions */
324		}
325
326/*
327 *		See if we are done chaining steps
328 */
329
330		if((pnstate->pmsSetCmd == pmsDelay)
331			|| (!(pp->pms.pmsCSetCmd & pmsSync) && (pnstate->pmsLimit != 0))) {	/* Is this not syncronous and a non-zero delay or a delayed step? */
332			etimer_resync_deadlines();							/* Start the timers ticking */
333			break;									/* We've stepped as far as we're going to... */
334		}
335
336		nstep = pnstate->pmsNext;					/* Chain on to the next */
337	}
338}
339
340/*
341 *	Either park the stepper or force the step on a parked stepper for local processor only
342 *
343 */
344
345void
346pmsRunLocal(uint32_t nstep)
347{
348	PER_PROC_INFO *pp;
349	uint32_t lastState;
350	int cpu, i;
351	boolean_t	intr;
352
353	if(!pmsInstalled) /* Ignore this if no step programs installed... */
354		return;
355
356	intr = ml_set_interrupts_enabled(FALSE);		/* No interruptions in here */
357
358	pp = GET_PER_PROC_INFO();								/* Get our per_proc */
359
360	if(nstep == pmsStartUp) {						/* Should we start up? */
361		pmsCPUInit();								/* Get us up to full with high voltage and park */
362		nstep = pmsNormHigh;						/* Change request to transition to normal high */
363	}
364
365	lastState = pp->pms.pmsState;					/* Remember if we are parked now */
366
367	pmsSetStep(nstep, 1);							/* Step to the new state */
368
369	if((lastState == pmsParked) && (pp->pms.pmsState != pmsParked)) {	/* Did we just unpark? */
370		cpu = cpu_number();							/* Get our processor */
371		for(i = 0; i < pmsMaxStates; i++) {			/* Step through the steps and clear the statistics since we were parked */
372			pmsCtls.pmsStats[cpu][i].stTime[0] = 0;	/* Clear accumulated time - downward */
373			pmsCtls.pmsStats[cpu][i].stTime[1] = 0;	/* Clear accumulated time - forward */
374			pmsCtls.pmsStats[cpu][i].stCnt[0] = 0;	/* Clear transition count - downward */
375			pmsCtls.pmsStats[cpu][i].stCnt[1] = 0;	/* Clear transition count - forward */
376		}
377	}
378
379	(void)ml_set_interrupts_enabled(intr);			/* Restore interruptions */
380}
381
382/*
383 *	Control the Power Management Stepper.
384 *	Called from user state by the superuser.
385 *	Interruptions disabled.
386 *
387 */
388kern_return_t
389pmsControl(uint32_t request, user_addr_t reqaddr, uint32_t reqsize)
390{
391	uint32_t nstep = 0, result, presult;
392	int ret, cpu;
393	kern_return_t kret = KERN_SUCCESS;
394	pmsDef *ndefs;
395	PER_PROC_INFO *pp;
396
397	pp = GET_PER_PROC_INFO();								/* Get our per_proc */
398	cpu = cpu_number();								/* Get our processor */
399
400	if(!is_suser()) {								/* We are better than most, */
401		kret = KERN_FAILURE;
402		goto out;
403	}
404
405	if(request >= pmsCFree) {					/* Can we understand the request? */
406		kret = KERN_INVALID_ARGUMENT;
407		goto out;
408	}
409
410	if(request == pmsCQuery) {						/* Are we just checking? */
411		result = pmsCPUQuery() & pmsCPU;			/* Get the processor data and make sure there is no slop */
412		presult = 0;								/* Assume nothing */
413		if((uint32_t)pmsQueryFunc)
414			presult = pmsQueryFunc(cpu, pmsPlatformData);	/* Go get the platform state */
415		kret = result | (presult & (pmsXClk | pmsVoltage | pmsPowerID));	/* Merge the platform state with no slop */
416		goto out;
417	}
418
419	if(request == pmsCExperimental) {				/* Enter experimental mode? */
420
421		if(pmsInstalled || (pmsExperimental & 1)) {	/* Are we already running or in experimental? */
422			kret = KERN_FAILURE;
423			goto out;
424		}
425
426		pmsExperimental |= 1;						/* Flip us into experimental but don't change other flags */
427
428		pmsCPUConf();								/* Configure for this machine */
429		pmsStart();									/* Start stepping */
430		goto out;
431	}
432
433	if(request == pmsCCnfg) {						/* Do some up-front checking before we commit to doing this */
434		if((reqsize > (pmsMaxStates * sizeof(pmsDef))) || (reqsize < (pmsFree * sizeof(pmsDef)))) {	/* Check that the size is reasonable */
435			kret = KERN_NO_SPACE;
436			goto out;
437		}
438	}
439
440	if (request == pmsGCtls) {
441		if (reqsize != sizeof(pmsCtls)) {
442			kret = KERN_FAILURE;
443			goto out;
444		}
445		ret = copyout(&pmsCtls, reqaddr, reqsize);
446		goto out;
447	}
448
449	if (request == pmsGStats) {
450		if (reqsize != sizeof(pmsStatsd)) { /* request size is fixed */
451			kret = KERN_FAILURE;
452			goto out;
453		}
454		ret = copyout(&pmsStatsd, reqaddr, reqsize);
455		goto out;
456	}
457
458/*
459 *	We are committed after here.  If there are any errors detected, we shouldn't die, but we
460 *	will be stuck in park.
461 *
462 *	Also, we can possibly end up on another processor after the broadcast.
463 *
464 */
465
466	if(!hw_compare_and_store(0, 1, &pmsSyncrolator)) {	/* Are we already doing this? */
467		/* Tell them that we are already busy and to try again */
468		kret = KERN_RESOURCE_SHORTAGE;
469		goto out;
470	}
471
472//	NOTE:  We will block in the following code until everyone has finished the prepare
473
474	pmsRun(pmsPrepCng);								/* Get everyone parked and in a proper state for step table changes, including me */
475
476	if(request == pmsCPark) {						/* Is all we're supposed to do park? */
477		pmsSyncrolator = 0;							/* Free us up */
478		goto out;
479	}
480
481	switch(request) {								/* Select the routine */
482
483		case pmsCStart:								/* Starts normal steppping */
484			nstep = pmsNormHigh;					/* Set the request */
485			break;
486
487		case pmsCFLow:								/* Forces low power */
488			nstep = pmsLow;							/* Set request */
489			break;
490
491		case pmsCFHigh:								/* Forces high power */
492			nstep = pmsHigh;						/* Set request */
493			break;
494
495		case pmsCCnfg:								/* Loads new stepper program */
496
497			if(!(ndefs = (pmsDef *)kalloc(reqsize))) {	/* Get memory for the whole thing */
498				pmsSyncrolator = 0;					/* Free us up */
499				kret = KERN_INVALID_ADDRESS;
500				goto out;
501			}
502
503			ret = copyin(reqaddr, (void *)ndefs, reqsize);	/* Get the new config table */
504			if(ret) {								/* Hmmm, something went wrong with the copyin */
505				kfree(ndefs, reqsize);	/* Free up the copied in data */
506				pmsSyncrolator = 0;					/* Free us up */
507				kret = KERN_INVALID_ADDRESS;
508				goto out;
509			}
510
511			kret = pmsBuild(ndefs, reqsize, NULL, 0, NULL);	/* Go build and replace the tables.  Make sure we keep the old platform stuff */
512			if(kret) {								/* Hmmm, something went wrong with the compilation */
513				kfree(ndefs, reqsize);	/* Free up the copied in data */
514				pmsSyncrolator = 0;					/* Free us up */
515				goto out;
516			}
517
518			nstep = pmsNormHigh;					/* Set the request */
519			break;
520
521		default:
522			panic("pmsCntrl: stepper control is so very, very confused = %08X\n", request);
523
524	}
525
526	pmsRun(nstep);									/* Get everyone into step */
527	pmsSyncrolator = 0;								/* Free us up */
528out:
529	return kret;
530
531}
532
533/*
534 *	Broadcast a change to all processors including ourselves.
535 *
536 *	Interruptions are disabled.
537 */
538
539void
540pmsRun(uint32_t nstep)
541{
542	pmsCPURun(nstep);
543}
544
545
546/*
547 *	Build the tables needed for the stepper.  This includes both the step definitions and the step control table.
548 *
549 *	We most absolutely need to be parked before this happens because we're gonna change the table.
550 *	We're going to have to be pretty complete about checking for errors.
551 *	Also, a copy is always made because we don't want to be crippled by not being able to change
552 *	the table or description formats.
553 *
554 *	We pass in a table of external functions and the new stepper def uses the corresponding
555 *	indexes rather than actual function addresses.  This is done so that a proper table can be
556 *	built with the control syscall.  It can't supply addresses, so the index has to do.  We
557 *	internalize the table so our caller does not need to keep it.  Note that passing in a 0
558 *	will use the current function table.  Also note that entry 0 is reserved and must be 0,
559 *	we will check and fail the build.
560 *
561 *	The platformData parameter is a 32-bit word of data that is passed unaltered to the set function.
562 *
563 *	The queryFunc parameter is the address of a function that will return the current state of the platform.
564 *	The format of the data returned is the same as the platform specific portions of pmsSetCmd, i.e., pmsXClk,
565 *	pmsVoltage, and any part of pmsPowerID that is maintained by the platform hardware (an example would be
566 *	the values of the gpios that correspond to pmsPowerID).  The value should be constructed by querying
567 *	hardware rather than returning a value cached by software. One of the intents of this function is to
568 *	help recover lost or determine initial power states.
569 *
570 */
571
572kern_return_t pmsBuild(pmsDef *pd, uint32_t pdsize, pmsSetFunc_t *functab, uint32_t platformData, pmsQueryFunc_t queryFunc) {
573
574	int newsize, cstp, oldAltSize, xdsply;
575	uint32_t setf, steps, i, nstps;
576	uint64_t nlimit;
577	pmsDef *newpd, *oldAlt;
578	boolean_t intr;
579
580	xdsply = (pmsExperimental & 3) != 0;			/* Turn on kprintfs if requested or in experimental mode */
581
582	if(pdsize % sizeof(pmsDef))
583		return KERN_INVALID_ARGUMENT;	/* Length not multiple of definition size */
584
585	steps = pdsize / sizeof(pmsDef);				/* Get the number of steps supplied */
586
587	if((steps >= pmsMaxStates) || (steps < pmsFree))	/* Complain if too big or too small */
588		return KERN_INVALID_ARGUMENT;			/* Squeak loudly!!! */
589
590	if((uint32_t)functab && (uint32_t)functab[0])	/* Verify that if they supplied a new function table, entry 0 is 0 */
591		return KERN_INVALID_ARGUMENT;				/* Fail because they didn't reserve entry 0 */
592
593	if(xdsply) kprintf("\n  StepID   Down   Next    HWSel  HWfun                Limit\n");
594
595	for(i = 0; i < steps; i++) {					/* Step through and verify the definitions */
596
597		if(xdsply) kprintf("  %6d %6d %6d %08X %6d %20lld\n", pd[i].pmsStepID, pd[i].pmsDown,
598			pd[i].pmsNext, pd[i].pmsSetCmd,
599			pd[i].sf.pmsSetFuncInd, pd[i].pmsLimit);
600
601		if((pd[i].pmsLimit != 0) && (pd[i].pmsLimit < 100ULL)) {
602			if(xdsply) kprintf("error step %3d: pmsLimit too small/n", i);
603			return KERN_INVALID_ARGUMENT;	/* Has to be 100�S or more */
604		}
605
606		if((pd[i].pmsLimit != 0xFFFFFFFFFFFFFFFFULL) && (pd[i].pmsLimit > (HalfwayToForever / 1000ULL))) {
607			if(xdsply) kprintf("error step %3d: pmsLimit too big\n", i);
608			return KERN_INVALID_ARGUMENT;			/* Can't be too big */
609		}
610
611		if(pd[i].pmsStepID != i) {
612			if(xdsply) kprintf("error step %3d: step ID does not match (%d)\n", i, pd[i].pmsStepID);
613			return KERN_INVALID_ARGUMENT;	/* ID must match */
614		}
615
616		if(pd[i].sf.pmsSetFuncInd >= pmsSetFuncMax) {
617			if(xdsply) kprintf("error step %3d: function invalid (%d)\n", i, pd[i].sf.pmsSetFuncInd);
618			return KERN_INVALID_ARGUMENT;	/* Fail if this function is not in the table */
619		}
620
621		if((pd[i].pmsDown != pmsParked) && pd[i].pmsDown >= steps) {
622			if(xdsply) kprintf("error step %3d: pmsDown out of range (%d)\n", i, pd[i].pmsDown);
623			return KERN_INVALID_ARGUMENT;	/* Step down must be in the table or park */
624		}
625
626		if((pd[i].pmsNext != pmsParked) && pd[i].pmsNext >= steps) {
627			if(xdsply) kprintf("error step %3d: pmsNext out of range (%d)\n", i, pd[i].pmsNext);
628			return KERN_INVALID_ARGUMENT;	/* Step up must be in the table or park */
629		}
630
631		if((pd[i].pmsSetCmd == pmsDelay) && (pd[i].pmsTDelay >= steps)) {
632			if(xdsply) kprintf("error step %3d: pmsTDelay out of range (%d)\n", i, pd[i].pmsTDelay);
633			return KERN_INVALID_ARGUMENT;	/* Delayed step must be in the table */
634		}
635
636		if((pd[i].pmsSetCmd == pmsDelay) && (pd[i].pmsLimit == 0xFFFFFFFFFFFFFFFFULL)) {
637			if(xdsply) kprintf("error step %3d: delay time limit must not be infinite\n", i);
638			return KERN_INVALID_ARGUMENT;	/* Delayed step must have a time limit */
639		}
640
641	}
642
643/*
644 *	Verify that there are no infinite synchronous forward loops in the table
645 */
646
647	if(xdsply) kprintf("\nInitial scan passed, start in loop check\n");
648	for(i = 0; i < steps; i++) {					/* Start with each step. Inefficient, but who cares */
649
650		cstp = i;									/* Set starting point */
651		nstps = 0;									/* Initialize chain length counter */
652		while(1) {									/* Do until we hit the end */
653			if(pd[cstp].pmsSetCmd == pmsParkIt) break;	/* Parking always terminates a chain so no endless loop here */
654			if(pd[cstp].pmsSetCmd == pmsDelay) break;	/* Delayed steps always terminate a chain so no endless loop here */
655			if((pd[cstp].pmsLimit != 0) && ((pd[cstp].pmsSetCmd & pmsSync) != pmsSync)) break;	/* If time limit is not 0 and not synchrouous, no endless loop */
656			if(pd[cstp].pmsNext == pmsParked) break;	/* If the next step is parked, no endless loop */
657
658 			cstp = pd[cstp].pmsNext;				/* Chain to the next */
659 			nstps = nstps + 1;						/* Count this step */
660 			if(nstps >= steps) {					/* We've stepped for more steps than we have, must be an endless loop! */
661				if(xdsply) kprintf("error step %3d: infinite pmsNext loop\n", i);
662		 		return KERN_INVALID_ARGUMENT;		/* Suggest to our caller that they can't program... */
663 			}
664 		}
665	}
666
667	if((pmsExperimental & 4) && (pmsInstalled) && ((uint32_t)functab != 0)) {	/* If we are already initted and experimental is locked in, and we are doing first */
668		if(xdsply) kprintf("Experimental locked, ignoring driver pmsBuild\n");
669		return KERN_RESOURCE_SHORTAGE;				/* Just ignore the request. */
670	}
671
672
673
674/*
675 *	Well, things look ok, let's do it to it...
676 */
677
678	if(xdsply) kprintf("Loop check passed, building and installing table\n");
679
680	newsize = steps * sizeof(pmsDef);				/* Get the size needed for the definition blocks */
681
682	if(!(newpd = (pmsDef *)kalloc(newsize))) {		/* Get memory for the whole thing */
683		return KERN_RESOURCE_SHORTAGE;				/* No storage... */
684	}
685
686	bzero((void *)newpd, newsize);					/* Make it pretty */
687
688/*
689 *	Ok, this is it, finish intitializing, switch the tables, and pray...
690 *	We want no interruptions at all and we need to lock the table.  Everybody should be parked,
691 *	so no one should ever touch this.  The lock is to keep multiple builders safe.  It probably
692 *	will never ever happen, but paranoia is a good thing...
693 */
694
695	intr = ml_set_interrupts_enabled(FALSE);		/* No interruptions in here */
696	simple_lock(&pmsBuildLock);						/* Lock out everyone... */
697
698	if(platformData) pmsPlatformData = platformData;	/* Remember the platform data word passed in if any was... */
699	if((uint32_t)queryFunc) pmsQueryFunc = queryFunc;	/* Remember the query function passed in, if it was... */
700
701	oldAlt = altDpmsTab;							/* Remember any old alternate we had */
702	oldAltSize = altDpmsTabSize;					/* Remember its size */
703
704	altDpmsTab = newpd;								/* Point to the new table */
705	altDpmsTabSize = newsize;						/* Set the size */
706
707	if((uint32_t)functab) {							/* Did we get a new function table? */
708		for(i = 0; i < pmsSetFuncMax; i++) pmsFuncTab[i] = functab[i];	/* Copy in the new table */
709	}
710
711	for(i = 0; i < pmsMaxStates; i++) pmsCtls.pmsDefs[i] = &pmsDummy;	/* Initialize the table to point to the dummy step */
712
713	for(i = 0; i < steps; i++) {					/* Replace the step table entries */
714		if(pd[i].pmsLimit == 0xFFFFFFFFFFFFFFFFULL) nlimit = century;	/* Default to 100 years */
715		else nlimit = pd[i].pmsLimit;				/* Otherwise use what was supplied */
716
717		nanoseconds_to_absolutetime(nlimit * 1000ULL, &newpd[i].pmsLimit);	/* Convert microseconds to nanoseconds and then to ticks */
718
719		setf = pd[i].sf.pmsSetFuncInd;					/* Make convienient */
720		newpd[i].sf.pmsSetFunc = pmsFuncTab[setf];		/* Replace the index with the function address */
721
722		newpd[i].pmsStepID  = pd[i].pmsStepID;		/* Set the step ID */
723		newpd[i].pmsSetCmd  = pd[i].pmsSetCmd;		/* Set the hardware selector ID */
724		newpd[i].pmsDown    = pd[i].pmsDown;		/* Set the downward step */
725		newpd[i].pmsNext    = pd[i].pmsNext;		/* Set the next setp */
726		newpd[i].pmsTDelay  = pd[i].pmsTDelay;		/* Set the delayed setp */
727		pmsCtls.pmsDefs[i]  = &newpd[i];			/* Copy it in */
728	}
729#ifdef __ppc__
730	pmsCtlp = (uint32_t)&pmsCtls;					/* Point to the new pms table */
731#endif
732 	pmsInstalled = 1;								/* The stepper has been born or born again... */
733
734	simple_unlock(&pmsBuildLock);					/* Free play! */
735	(void)ml_set_interrupts_enabled(intr);			/* Interrupts back the way there were */
736
737	if((uint32_t)oldAlt) /* If we already had an alternate, free it */
738		kfree(oldAlt, oldAltSize);
739
740	if(xdsply) kprintf("Stepper table installed\n");
741
742	return KERN_SUCCESS;							/* We're in fate's hands now... */
743}
744