1/*
2 * Copyright (c) 1999-2000 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/*
29 * Copyright (c) 1999-2000 Apple Computer, Inc.  All rights reserved.
30 *
31 *  DRI: Josh de Cesare
32 *
33 */
34
35extern "C" {
36#include <machine/machine_routines.h>
37#include <pexpert/pexpert.h>
38}
39
40#include <machine/machine_routines.h>
41
42#include <IOKit/IOLib.h>
43#include <IOKit/IOPlatformExpert.h>
44#include <IOKit/pwr_mgt/RootDomain.h>
45#include <IOKit/pwr_mgt/IOPMPrivate.h>
46#include <IOKit/IOUserClient.h>
47#include <IOKit/IOKitKeysPrivate.h>
48#include <IOKit/IOCPU.h>
49
50/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
51#include <kern/queue.h>
52
53typedef kern_return_t (*iocpu_platform_action_t)(void * refcon0, void * refcon1, uint32_t priority,
54						 void * param1, void * param2, void * param3,
55						 const char * name);
56
57struct iocpu_platform_action_entry
58{
59    queue_chain_t                     link;
60    iocpu_platform_action_t           action;
61    int32_t	                      priority;
62    const char *		      name;
63    void *	                      refcon0;
64    void *			      refcon1;
65    struct iocpu_platform_action_entry * alloc_list;
66};
67typedef struct iocpu_platform_action_entry iocpu_platform_action_entry_t;
68
69queue_head_t *
70iocpu_get_platform_quiesce_queue(void);
71
72queue_head_t *
73iocpu_get_platform_active_queue(void);
74
75void
76iocpu_platform_cpu_action_init(queue_head_t * quiesce_queue, queue_head_t * init_queue);
77
78void
79iocpu_add_platform_action(queue_head_t * queue, iocpu_platform_action_entry_t * entry);
80
81void
82iocpu_remove_platform_action(iocpu_platform_action_entry_t * entry);
83
84kern_return_t
85iocpu_run_platform_actions(queue_head_t * queue, uint32_t first_priority, uint32_t last_priority,
86					void * param1, void * param2, void * param3);
87
88/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
89
90#define kBootCPUNumber  0
91
92static iocpu_platform_action_entry_t * gIOAllActionsQueue;
93static queue_head_t gIOSleepActionQueue;
94static queue_head_t gIOWakeActionQueue;
95
96static queue_head_t iocpu_quiesce_queue;
97static queue_head_t iocpu_active_queue;
98
99static queue_head_t gIOHaltRestartActionQueue;
100
101/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
102
103void
104iocpu_platform_cpu_action_init(queue_head_t * quiesce_queue, __unused queue_head_t * init_queue)
105{
106#if 0
107    enum { kNumQuiesceActions = 2 };
108    static iocpu_platform_action_entry_t quiesce_actions[kNumQuiesceActions] =
109    {
110	{ { NULL, NULL }, (iocpu_platform_action_t) &clean_mmu_dcache, 97000, 0, 0, NULL },
111	{ { NULL, NULL }, (iocpu_platform_action_t) &arm_sleep, 99000, 0, 0, NULL },
112    };
113    unsigned int idx;
114
115    for (idx = 0; idx < kNumQuiesceActions; idx++)
116	iocpu_add_platform_action(quiesce_queue, &quiesce_actions[idx]);
117#endif
118}
119
120queue_head_t * iocpu_get_platform_quiesce_queue(void)
121{
122    if (!iocpu_quiesce_queue.next)
123    {
124	queue_init(&iocpu_quiesce_queue);
125	queue_init(&iocpu_active_queue);
126	iocpu_platform_cpu_action_init(&iocpu_quiesce_queue, &iocpu_active_queue);
127    }
128    return (&iocpu_quiesce_queue);
129}
130
131queue_head_t * iocpu_get_platform_active_queue(void)
132{
133    if (!iocpu_active_queue.next)
134    {
135	queue_init(&iocpu_quiesce_queue);
136	queue_init(&iocpu_active_queue);
137	iocpu_platform_cpu_action_init(&iocpu_quiesce_queue, &iocpu_active_queue);
138    }
139    return (&iocpu_active_queue);
140}
141
142void iocpu_add_platform_action(queue_head_t * queue, iocpu_platform_action_entry_t * entry)
143{
144    iocpu_platform_action_entry_t * next;
145
146    queue_iterate(queue, next, iocpu_platform_action_entry_t *, link)
147    {
148	if (next->priority > entry->priority)
149	{
150	    queue_insert_before(queue, entry, next, iocpu_platform_action_entry_t *, link);
151	    return;
152	}
153    }
154    queue_enter(queue, entry, iocpu_platform_action_entry_t *, link);	// at tail
155}
156
157void iocpu_remove_platform_action(iocpu_platform_action_entry_t * entry)
158{
159    remque(&entry->link);
160}
161
162kern_return_t
163iocpu_run_platform_actions(queue_head_t * queue, uint32_t first_priority, uint32_t last_priority,
164					void * param1, void * param2, void * param3)
165{
166    kern_return_t                ret = KERN_SUCCESS;
167    kern_return_t                result = KERN_SUCCESS;
168    iocpu_platform_action_entry_t * next;
169
170    queue_iterate(queue, next, iocpu_platform_action_entry_t *, link)
171    {
172	uint32_t pri = (next->priority < 0) ? -next->priority : next->priority;
173	if ((pri >= first_priority) && (pri <= last_priority))
174	{
175	    //kprintf("[%p]", next->action);
176	    ret = (*next->action)(next->refcon0, next->refcon1, pri, param1, param2, param3, next->name);
177	}
178	if (KERN_SUCCESS == result)
179	    result = ret;
180    }
181    return (result);
182}
183
184/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
185
186extern "C" kern_return_t
187IOCPURunPlatformQuiesceActions(void)
188{
189    return (iocpu_run_platform_actions(iocpu_get_platform_quiesce_queue(), 0, 0U-1,
190				    NULL, NULL, NULL));
191}
192
193extern "C" kern_return_t
194IOCPURunPlatformActiveActions(void)
195{
196    return (iocpu_run_platform_actions(iocpu_get_platform_active_queue(), 0, 0U-1,
197				    NULL, NULL, NULL));
198}
199
200static kern_return_t
201IOServicePlatformAction(void * refcon0, void * refcon1, uint32_t priority,
202			  void * param1, void * param2, void * param3,
203			  const char * service_name)
204{
205    IOReturn	     ret;
206    IOService *      service  = (IOService *)      refcon0;
207    const OSSymbol * function = (const OSSymbol *) refcon1;
208
209    kprintf("%s -> %s\n", function->getCStringNoCopy(), service_name);
210
211    ret = service->callPlatformFunction(function, false,
212					 (void *)(uintptr_t) priority, param1, param2, param3);
213
214    return (ret);
215}
216
217static void
218IOInstallServicePlatformAction(IOService * service,
219				const OSSymbol * key, queue_head_t * queue,
220				bool reverse)
221{
222    OSNumber * num;
223    iocpu_platform_action_entry_t * entry;
224    uint32_t priority;
225
226    num = OSDynamicCast(OSNumber, service->getProperty(key));
227    if (!num)
228	return;
229
230    entry = IONew(iocpu_platform_action_entry_t, 1);
231    entry->action = &IOServicePlatformAction;
232    entry->name = service->getName();
233    priority = num->unsigned32BitValue();
234    if (reverse)
235	entry->priority = -priority;
236    else
237	entry->priority = priority;
238    entry->refcon0 = service;
239    entry->refcon1 = (void *) key;
240
241    iocpu_add_platform_action(queue, entry);
242    entry->alloc_list = gIOAllActionsQueue;
243    gIOAllActionsQueue = entry;
244}
245
246extern "C" kern_return_t
247IOCPURunPlatformHaltRestartActions(uint32_t message)
248{
249    kern_return_t	 ret;
250    IORegistryIterator * iter;
251    OSOrderedSet *       all;
252    IOService *          service;
253
254    if (!gIOHaltRestartActionQueue.next)
255    {
256	queue_init(&gIOHaltRestartActionQueue);
257	iter = IORegistryIterator::iterateOver(gIOServicePlane,
258						kIORegistryIterateRecursively);
259	if (iter)
260	{
261	    all = 0;
262	    do
263	    {
264		if (all) all->release();
265		all = iter->iterateAll();
266	    }
267	    while (!iter->isValid());
268	    iter->release();
269	    if (all)
270	    {
271		while((service = (IOService *) all->getFirstObject()))
272		{
273		    IOInstallServicePlatformAction(service, gIOPlatformHaltRestartActionKey, &gIOHaltRestartActionQueue, false);
274		    all->removeObject(service);
275		}
276		all->release();
277	    }
278	}
279    }
280    ret = iocpu_run_platform_actions(&gIOHaltRestartActionQueue, 0, 0U-1,
281				     (void *)(uintptr_t) message, NULL, NULL);
282    return (ret);
283}
284
285/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
286
287kern_return_t PE_cpu_start(cpu_id_t target,
288			   vm_offset_t start_paddr, vm_offset_t arg_paddr)
289{
290  IOCPU *targetCPU = OSDynamicCast(IOCPU, (OSObject *)target);
291
292  if (targetCPU == 0) return KERN_FAILURE;
293  return targetCPU->startCPU(start_paddr, arg_paddr);
294}
295
296void PE_cpu_halt(cpu_id_t target)
297{
298  IOCPU *targetCPU = OSDynamicCast(IOCPU, (OSObject *)target);
299
300  if (targetCPU) targetCPU->haltCPU();
301}
302
303void PE_cpu_signal(cpu_id_t source, cpu_id_t target)
304{
305  IOCPU *sourceCPU = OSDynamicCast(IOCPU, (OSObject *)source);
306  IOCPU *targetCPU = OSDynamicCast(IOCPU, (OSObject *)target);
307
308  if (sourceCPU && targetCPU) sourceCPU->signalCPU(targetCPU);
309}
310
311void PE_cpu_machine_init(cpu_id_t target, boolean_t bootb)
312{
313  IOCPU *targetCPU = OSDynamicCast(IOCPU, (OSObject *)target);
314
315  if (targetCPU) targetCPU->initCPU(bootb);
316}
317
318void PE_cpu_machine_quiesce(cpu_id_t target)
319{
320  IOCPU *targetCPU = OSDynamicCast(IOCPU, (OSObject *)target);
321
322  if (targetCPU) targetCPU->quiesceCPU();
323}
324
325
326/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
327
328#define super IOService
329
330OSDefineMetaClassAndAbstractStructors(IOCPU, IOService);
331OSMetaClassDefineReservedUnused(IOCPU, 0);
332OSMetaClassDefineReservedUnused(IOCPU, 1);
333OSMetaClassDefineReservedUnused(IOCPU, 2);
334OSMetaClassDefineReservedUnused(IOCPU, 3);
335OSMetaClassDefineReservedUnused(IOCPU, 4);
336OSMetaClassDefineReservedUnused(IOCPU, 5);
337OSMetaClassDefineReservedUnused(IOCPU, 6);
338OSMetaClassDefineReservedUnused(IOCPU, 7);
339
340/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
341
342static OSArray *gIOCPUs;
343static const OSSymbol *gIOCPUStateKey;
344static OSString *gIOCPUStateNames[kIOCPUStateCount];
345
346void IOCPUSleepKernel(void)
347{
348    long cnt, numCPUs;
349    IOCPU *target;
350    IOCPU *bootCPU = NULL;
351    IOPMrootDomain  *rootDomain = IOService::getPMRootDomain();
352
353    kprintf("IOCPUSleepKernel\n");
354
355    IORegistryIterator * iter;
356    OSOrderedSet *       all;
357    IOService *          service;
358
359    rootDomain->tracePoint( kIOPMTracePointSleepPlatformActions );
360
361    queue_init(&gIOSleepActionQueue);
362    queue_init(&gIOWakeActionQueue);
363    queue_init(&gIOHaltRestartActionQueue);
364
365    iter = IORegistryIterator::iterateOver( gIOServicePlane,
366					    kIORegistryIterateRecursively );
367    if( iter)
368    {
369	all = 0;
370	do
371	{
372	    if (all)
373		all->release();
374	    all = iter->iterateAll();
375	}
376	while (!iter->isValid());
377	iter->release();
378
379	if (all)
380	{
381	    while((service = (IOService *) all->getFirstObject()))
382	    {
383		IOInstallServicePlatformAction(service, gIOPlatformSleepActionKey,   &gIOSleepActionQueue,		 false);
384		IOInstallServicePlatformAction(service, gIOPlatformWakeActionKey,    &gIOWakeActionQueue,		 true);
385		IOInstallServicePlatformAction(service, gIOPlatformQuiesceActionKey, iocpu_get_platform_quiesce_queue(), false);
386		IOInstallServicePlatformAction(service, gIOPlatformActiveActionKey,  iocpu_get_platform_active_queue(),  true);
387		IOInstallServicePlatformAction(service, gIOPlatformHaltRestartActionKey, &gIOHaltRestartActionQueue,     false);
388		all->removeObject(service);
389	    }
390	    all->release();
391	}
392    }
393
394    iocpu_run_platform_actions(&gIOSleepActionQueue, 0, 0U-1,
395				NULL, NULL, NULL);
396
397    rootDomain->tracePoint( kIOPMTracePointSleepCPUs );
398
399    numCPUs = gIOCPUs->getCount();
400    // Sleep the CPUs.
401    cnt = numCPUs;
402    while (cnt--)
403    {
404        target = OSDynamicCast(IOCPU, gIOCPUs->getObject(cnt));
405
406        // We make certain that the bootCPU is the last to sleep
407        // We'll skip it for now, and halt it after finishing the
408        // non-boot CPU's.
409        if (target->getCPUNumber() == kBootCPUNumber)
410        {
411            bootCPU = target;
412        } else if (target->getCPUState() == kIOCPUStateRunning)
413        {
414            target->haltCPU();
415        }
416    }
417
418    rootDomain->tracePoint( kIOPMTracePointSleepPlatformDriver );
419
420    // Now sleep the boot CPU.
421    if (bootCPU)
422        bootCPU->haltCPU();
423
424    rootDomain->tracePoint( kIOPMTracePointWakePlatformActions );
425
426    iocpu_run_platform_actions(&gIOWakeActionQueue, 0, 0U-1,
427				    NULL, NULL, NULL);
428
429    iocpu_platform_action_entry_t * entry;
430    while ((entry = gIOAllActionsQueue))
431    {
432	gIOAllActionsQueue = entry->alloc_list;
433	iocpu_remove_platform_action(entry);
434	IODelete(entry, iocpu_platform_action_entry_t, 1);
435    }
436
437    if (!queue_empty(&gIOSleepActionQueue))       panic("gIOSleepActionQueue");
438    if (!queue_empty(&gIOWakeActionQueue))  	  panic("gIOWakeActionQueue");
439    if (!queue_empty(&gIOHaltRestartActionQueue)) panic("gIOHaltRestartActionQueue");
440    gIOHaltRestartActionQueue.next = 0;
441
442    rootDomain->tracePoint( kIOPMTracePointWakeCPUs );
443
444    // Wake the other CPUs.
445    for (cnt = 0; cnt < numCPUs; cnt++)
446    {
447        target = OSDynamicCast(IOCPU, gIOCPUs->getObject(cnt));
448
449        // Skip the already-woken boot CPU.
450        if ((target->getCPUNumber() != kBootCPUNumber)
451            && (target->getCPUState() == kIOCPUStateStopped))
452        {
453            processor_start(target->getMachProcessor());
454        }
455    }
456}
457
458void IOCPU::initCPUs(void)
459{
460  if (gIOCPUs == 0) {
461    gIOCPUs = OSArray::withCapacity(1);
462
463    gIOCPUStateKey = OSSymbol::withCStringNoCopy("IOCPUState");
464
465    gIOCPUStateNames[kIOCPUStateUnregistered] =
466      OSString::withCStringNoCopy("Unregistered");
467    gIOCPUStateNames[kIOCPUStateUninitalized] =
468      OSString::withCStringNoCopy("Uninitalized");
469    gIOCPUStateNames[kIOCPUStateStopped] =
470      OSString::withCStringNoCopy("Stopped");
471    gIOCPUStateNames[kIOCPUStateRunning] =
472      OSString::withCStringNoCopy("Running");
473  }
474}
475
476bool IOCPU::start(IOService *provider)
477{
478  OSData *busFrequency, *cpuFrequency, *timebaseFrequency;
479
480  if (!super::start(provider)) return false;
481
482  initCPUs();
483
484  _cpuGroup = gIOCPUs;
485  cpuNub = provider;
486
487  gIOCPUs->setObject(this);
488
489  // Correct the bus, cpu and timebase frequencies in the device tree.
490  if (gPEClockFrequencyInfo.bus_frequency_hz < 0x100000000ULL) {
491    busFrequency = OSData::withBytesNoCopy((void *)&gPEClockFrequencyInfo.bus_clock_rate_hz, 4);
492  } else {
493    busFrequency = OSData::withBytesNoCopy((void *)&gPEClockFrequencyInfo.bus_frequency_hz, 8);
494  }
495  provider->setProperty("bus-frequency", busFrequency);
496  busFrequency->release();
497
498  if (gPEClockFrequencyInfo.cpu_frequency_hz < 0x100000000ULL) {
499    cpuFrequency = OSData::withBytesNoCopy((void *)&gPEClockFrequencyInfo.cpu_clock_rate_hz, 4);
500  } else {
501    cpuFrequency = OSData::withBytesNoCopy((void *)&gPEClockFrequencyInfo.cpu_frequency_hz, 8);
502  }
503  provider->setProperty("clock-frequency", cpuFrequency);
504  cpuFrequency->release();
505
506  timebaseFrequency = OSData::withBytesNoCopy((void *)&gPEClockFrequencyInfo.timebase_frequency_hz, 4);
507  provider->setProperty("timebase-frequency", timebaseFrequency);
508  timebaseFrequency->release();
509
510  super::setProperty("IOCPUID", getRegistryEntryID(), sizeof(uint64_t)*8);
511
512  setCPUNumber(0);
513  setCPUState(kIOCPUStateUnregistered);
514
515  return true;
516}
517
518OSObject *IOCPU::getProperty(const OSSymbol *aKey) const
519{
520  if (aKey == gIOCPUStateKey) return gIOCPUStateNames[_cpuState];
521
522  return super::getProperty(aKey);
523}
524
525bool IOCPU::setProperty(const OSSymbol *aKey, OSObject *anObject)
526{
527  OSString *stateStr;
528
529  if (aKey == gIOCPUStateKey) {
530    stateStr = OSDynamicCast(OSString, anObject);
531    if (stateStr == 0) return false;
532
533    if (_cpuNumber == 0) return false;
534
535    if (stateStr->isEqualTo("running")) {
536      if (_cpuState == kIOCPUStateStopped) {
537	processor_start(machProcessor);
538      } else if (_cpuState != kIOCPUStateRunning) {
539	return false;
540      }
541    } else if (stateStr->isEqualTo("stopped")) {
542      if (_cpuState == kIOCPUStateRunning) {
543        haltCPU();
544      } else if (_cpuState != kIOCPUStateStopped) {
545        return false;
546      }
547    } else return false;
548
549    return true;
550  }
551
552  return super::setProperty(aKey, anObject);
553}
554
555bool IOCPU::serializeProperties(OSSerialize *serialize) const
556{
557	bool result;
558	OSDictionary *dict = dictionaryWithProperties();
559	dict->setObject(gIOCPUStateKey, gIOCPUStateNames[_cpuState]);
560	result = dict->serialize(serialize);
561	dict->release();
562	return result;
563}
564
565IOReturn IOCPU::setProperties(OSObject *properties)
566{
567  OSDictionary *dict = OSDynamicCast(OSDictionary, properties);
568  OSString     *stateStr;
569  IOReturn     result;
570
571  if (dict == 0) return kIOReturnUnsupported;
572
573  stateStr = OSDynamicCast(OSString, dict->getObject(gIOCPUStateKey));
574  if (stateStr != 0) {
575    result = IOUserClient::clientHasPrivilege(current_task(), kIOClientPrivilegeAdministrator);
576    if (result != kIOReturnSuccess) return result;
577
578    if (setProperty(gIOCPUStateKey, stateStr)) return kIOReturnSuccess;
579
580    return kIOReturnUnsupported;
581  }
582
583  return kIOReturnUnsupported;
584}
585
586void IOCPU::signalCPU(IOCPU */*target*/)
587{
588}
589
590void IOCPU::enableCPUTimeBase(bool /*enable*/)
591{
592}
593
594UInt32 IOCPU::getCPUNumber(void)
595{
596  return _cpuNumber;
597}
598
599void IOCPU::setCPUNumber(UInt32 cpuNumber)
600{
601  _cpuNumber = cpuNumber;
602  super::setProperty("IOCPUNumber", _cpuNumber, 32);
603}
604
605UInt32 IOCPU::getCPUState(void)
606{
607  return _cpuState;
608}
609
610void IOCPU::setCPUState(UInt32 cpuState)
611{
612  if (cpuState < kIOCPUStateCount) {
613    _cpuState = cpuState;
614  }
615}
616
617OSArray *IOCPU::getCPUGroup(void)
618{
619  return _cpuGroup;
620}
621
622UInt32 IOCPU::getCPUGroupSize(void)
623{
624  return _cpuGroup->getCount();
625}
626
627processor_t IOCPU::getMachProcessor(void)
628{
629  return machProcessor;
630}
631
632
633/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
634
635#undef super
636#define super IOInterruptController
637
638OSDefineMetaClassAndStructors(IOCPUInterruptController, IOInterruptController);
639
640OSMetaClassDefineReservedUnused(IOCPUInterruptController, 0);
641OSMetaClassDefineReservedUnused(IOCPUInterruptController, 1);
642OSMetaClassDefineReservedUnused(IOCPUInterruptController, 2);
643OSMetaClassDefineReservedUnused(IOCPUInterruptController, 3);
644OSMetaClassDefineReservedUnused(IOCPUInterruptController, 4);
645OSMetaClassDefineReservedUnused(IOCPUInterruptController, 5);
646
647
648
649/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
650
651
652IOReturn IOCPUInterruptController::initCPUInterruptController(int sources)
653{
654  int cnt;
655
656  if (!super::init()) return kIOReturnInvalid;
657
658  numCPUs = sources;
659
660  cpus = (IOCPU **)IOMalloc(numCPUs * sizeof(IOCPU *));
661  if (cpus == 0) return kIOReturnNoMemory;
662  bzero(cpus, numCPUs * sizeof(IOCPU *));
663
664  vectors = (IOInterruptVector *)IOMalloc(numCPUs * sizeof(IOInterruptVector));
665  if (vectors == 0) return kIOReturnNoMemory;
666  bzero(vectors, numCPUs * sizeof(IOInterruptVector));
667
668  // Allocate locks for the
669  for (cnt = 0; cnt < numCPUs; cnt++) {
670    vectors[cnt].interruptLock = IOLockAlloc();
671    if (vectors[cnt].interruptLock == NULL) {
672      for (cnt = 0; cnt < numCPUs; cnt++) {
673	if (vectors[cnt].interruptLock != NULL)
674	  IOLockFree(vectors[cnt].interruptLock);
675      }
676      return kIOReturnNoResources;
677    }
678  }
679
680  ml_init_max_cpus(numCPUs);
681
682  return kIOReturnSuccess;
683}
684
685void IOCPUInterruptController::registerCPUInterruptController(void)
686{
687  registerService();
688
689  getPlatform()->registerInterruptController(gPlatformInterruptControllerName,
690					     this);
691}
692
693void IOCPUInterruptController::setCPUInterruptProperties(IOService *service)
694{
695  int          cnt;
696  OSArray      *controller;
697  OSArray      *specifier;
698  OSData       *tmpData;
699  long         tmpLong;
700
701  if ((service->getProperty(gIOInterruptControllersKey) != 0) &&
702      (service->getProperty(gIOInterruptSpecifiersKey) != 0))
703    return;
704
705  // Create the interrupt specifer array.
706  specifier = OSArray::withCapacity(numCPUs);
707  for (cnt = 0; cnt < numCPUs; cnt++) {
708    tmpLong = cnt;
709    tmpData = OSData::withBytes(&tmpLong, sizeof(tmpLong));
710    specifier->setObject(tmpData);
711    tmpData->release();
712  };
713
714  // Create the interrupt controller array.
715  controller = OSArray::withCapacity(numCPUs);
716  for (cnt = 0; cnt < numCPUs; cnt++) {
717    controller->setObject(gPlatformInterruptControllerName);
718  }
719
720  // Put the two arrays into the property table.
721  service->setProperty(gIOInterruptControllersKey, controller);
722  service->setProperty(gIOInterruptSpecifiersKey, specifier);
723  controller->release();
724  specifier->release();
725}
726
727void IOCPUInterruptController::enableCPUInterrupt(IOCPU *cpu)
728{
729	IOInterruptHandler handler = OSMemberFunctionCast(
730		IOInterruptHandler, this, &IOCPUInterruptController::handleInterrupt);
731
732	ml_install_interrupt_handler(cpu, cpu->getCPUNumber(), this, handler, 0);
733
734	// Ensure that the increment is seen by all processors
735	OSIncrementAtomic(&enabledCPUs);
736
737	if (enabledCPUs == numCPUs) thread_wakeup(this);
738}
739
740IOReturn IOCPUInterruptController::registerInterrupt(IOService *nub,
741						     int source,
742						     void *target,
743						     IOInterruptHandler handler,
744						     void *refCon)
745{
746  IOInterruptVector *vector;
747
748  if (source >= numCPUs) return kIOReturnNoResources;
749
750  vector = &vectors[source];
751
752  // Get the lock for this vector.
753  IOTakeLock(vector->interruptLock);
754
755  // Make sure the vector is not in use.
756  if (vector->interruptRegistered) {
757    IOUnlock(vector->interruptLock);
758    return kIOReturnNoResources;
759  }
760
761  // Fill in vector with the client's info.
762  vector->handler = handler;
763  vector->nub     = nub;
764  vector->source  = source;
765  vector->target  = target;
766  vector->refCon  = refCon;
767
768  // Get the vector ready.  It starts hard disabled.
769  vector->interruptDisabledHard = 1;
770  vector->interruptDisabledSoft = 1;
771  vector->interruptRegistered   = 1;
772
773  IOUnlock(vector->interruptLock);
774
775  if (enabledCPUs != numCPUs) {
776    assert_wait(this, THREAD_UNINT);
777    thread_block(THREAD_CONTINUE_NULL);
778  }
779
780  return kIOReturnSuccess;
781}
782
783IOReturn IOCPUInterruptController::getInterruptType(IOService */*nub*/,
784						    int /*source*/,
785						    int *interruptType)
786{
787  if (interruptType == 0) return kIOReturnBadArgument;
788
789  *interruptType = kIOInterruptTypeLevel;
790
791  return kIOReturnSuccess;
792}
793
794IOReturn IOCPUInterruptController::enableInterrupt(IOService */*nub*/,
795						   int /*source*/)
796{
797//  ml_set_interrupts_enabled(true);
798  return kIOReturnSuccess;
799}
800
801IOReturn IOCPUInterruptController::disableInterrupt(IOService */*nub*/,
802						    int /*source*/)
803{
804//  ml_set_interrupts_enabled(false);
805  return kIOReturnSuccess;
806}
807
808IOReturn IOCPUInterruptController::causeInterrupt(IOService */*nub*/,
809						  int /*source*/)
810{
811  ml_cause_interrupt();
812  return kIOReturnSuccess;
813}
814
815IOReturn IOCPUInterruptController::handleInterrupt(void */*refCon*/,
816						   IOService */*nub*/,
817						   int source)
818{
819  IOInterruptVector *vector;
820
821  vector = &vectors[source];
822
823  if (!vector->interruptRegistered) return kIOReturnInvalid;
824
825  vector->handler(vector->target, vector->refCon,
826		  vector->nub, vector->source);
827
828  return kIOReturnSuccess;
829}
830
831/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
832