|
|
1.1 root 1: /*
2: * Copyright (c) 1999 Apple Computer, Inc. All rights reserved.
3: *
4: * @APPLE_LICENSE_HEADER_START@
5: *
6: * Portions Copyright (c) 1999 Apple Computer, Inc. All Rights
7: * Reserved. This file contains Original Code and/or Modifications of
8: * Original Code as defined in and that are subject to the Apple Public
9: * Source License Version 1.1 (the "License"). You may not use this file
10: * except in compliance with the License. Please obtain a copy of the
11: * License at http://www.apple.com/publicsource and read it before using
12: * this file.
13: *
14: * The Original Code and all software distributed under the License are
15: * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
16: * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
17: * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
18: * FITNESS FOR A PARTICULAR PURPOSE OR NON- INFRINGEMENT. Please see the
19: * License for the specific language governing rights and limitations
20: * under the License.
21: *
22: * @APPLE_LICENSE_HEADER_END@
23: */
24: /*
25: * Copyright (c) 1993 NeXT Computer, Inc.
26: *
27: * ISA/EISA direct device implementation.
28: *
29: * HISTORY
30: *
31: * 10Jan93 Brian Pinkerton at NeXT
32: * Created.
33: *
34: *
35: * TODO:
36: * Implement IOPort reservation
37: * Verify that host-master DMA is working with DavidS
38: */
39:
40: #define KERNEL_PRIVATE 1
41:
42: #import <objc/List.h>
43: #import <driverkit/i386/directDevice.h>
44: #import <driverkit/IODirectDevicePrivate.h>
45: #import <driverkit/i386/IOEISADeviceDescription.h>
46: #import <driverkit/driverTypes.h>
47: #import <driverkit/i386/driverTypesPrivate.h>
48: #import <driverkit/generalFuncs.h>
49: #import <driverkit/i386/driverTypes.h>
50: #import <driverkit/i386/EISAKernBus.h>
51: #import <driverkit/KernDevice.h>
52: #import <driverkit/KernDeviceDescription.h>
53: #import <driverkit/kernelDriver.h>
54: #import <driverkit/interruptMsg.h>
55: #import <machdep/i386/dma_exported.h>
56: #import <machdep/i386/dma.h>
57: #import <machdep/i386/xpr.h>
58: #import <machkit/NXLock.h>
59: #import <kernserv/lock.h>
60: #import <kernserv/prototypes.h>
61:
62: #import <objc/HashTable.h>
63:
64: extern NXLock *dmaLock; // in autoconf_i386.m
65:
66: @interface IODirectDevice(EISAPrivate)
67:
68: - (unsigned int) _localToChannel:(unsigned int) localChannel;
69: - initEISA;
70: - freeEISA;
71:
72: @end
73:
74: struct _eisa_private {
75: Arch type;
76: };
77:
78: @implementation IODirectDevice(IOEISADirectDevice)
79:
80: - initEISA
81: {
82: struct _eisa_private *private;
83:
84: private = _busPrivate = (void *)IOMalloc(sizeof (*private));
85: private->type = EISA;
86:
87: return self;
88: }
89:
90: - freeEISA
91: {
92: struct _eisa_private *private = _busPrivate;
93:
94: IOFree((void *)private, sizeof (*private));
95:
96: return self;
97: }
98:
99: /*
100: * A bunch of convenient private API:
101: *
102: * Map from local channel numbers to real ones, and from local IRQ numbers
103: * to real ones.
104: */
105: - (unsigned int) _localToChannel:(unsigned int) localChannel
106: {
107: return [(IOEISADeviceDescription *)_deviceDescription channelList][localChannel];
108: }
109:
110:
111: /*
112: * Routines for dealing with interrupts start here.
113: */
114:
115: - (IOReturn) reserveInterrupt : (unsigned int)localInterrupt
116: {
117: return IO_R_SUCCESS;
118: }
119:
120: - (void) releaseInterrupt : (unsigned int) localInterrupt
121: {
122: }
123:
124:
125:
126:
127: /*
128: * The subclass overrides this method. In the superclass, we just
129: * return NO.
130: */
131: - (BOOL) getHandler:(IOEISAInterruptHandler *)handler
132: level:(unsigned int *)ipl
133: argument:(unsigned int *) arg
134: forInterrupt:(unsigned int) localInterrupt
135: {
136: return NO;
137: }
138:
139:
140: - (IOReturn) reserveChannel : (unsigned int) localChannel
141: {
142: return IO_R_SUCCESS;
143: }
144:
145: - (void) releaseChannel : (unsigned int) localChannel
146: {
147: }
148:
149: - (IOReturn) enableChannel:(unsigned int) localChannel
150: {
151: unsigned int realChannel;
152:
153: if (localChannel >= [(IOEISADeviceDescription *)_deviceDescription numChannels])
154: return IO_R_INVALID_ARG;
155:
156: realChannel = [self _localToChannel:localChannel];
157:
158: dma_unmask_chan(realChannel);
159: return IO_R_SUCCESS;
160: }
161:
162:
163: - (void) disableChannel:(unsigned int) localChannel
164: {
165: unsigned int realChannel;
166:
167: if (localChannel >= [(IOEISADeviceDescription *)_deviceDescription numChannels])
168: return;
169:
170: realChannel = [self _localToChannel:localChannel];
171:
172: dma_mask_chan(realChannel);
173: }
174:
175:
176: - (IOReturn) setTransferMode : (IODMATransferMode) mode
177: forChannel : (unsigned int) localChannel;
178: {
179: int real_mode;
180: unsigned int realChannel;
181:
182: if (localChannel >= [(IOEISADeviceDescription *)_deviceDescription numChannels])
183: return IO_R_INVALID_ARG;
184:
185: realChannel = [self _localToChannel:localChannel];
186:
187: switch(mode) {
188: case IO_Demand:
189: real_mode = DMA_MODE_DEMAND;
190: break;
191: case IO_Single:
192: real_mode = DMA_MODE_SINGLE;
193: break;
194: case IO_Block:
195: real_mode = DMA_MODE_BLOCK;
196: break;
197: case IO_Cascade:
198: real_mode = DMA_MODE_CASCADE;
199: break;
200: }
201: dma_chan_xfer_mode(realChannel, real_mode);
202: return IO_R_SUCCESS;
203: }
204:
205: /*
206: * Enable/disable autoinitialize DMA mode. Default is
207: * disabled.
208: */
209: - (IOReturn)setAutoinitialize : (BOOL)flag
210: forChannel : (unsigned)localChannel
211: {
212: unsigned int realChannel;
213:
214: if (localChannel >= [(IOEISADeviceDescription *)_deviceDescription numChannels])
215: return IO_R_INVALID_ARG;
216:
217: realChannel = [self _localToChannel:localChannel];
218:
219: dma_chan_autoinit(realChannel, (flag ? 1 : 0));
220: return IO_R_SUCCESS;
221: }
222:
223: /*
224: * Set DMA address increment/decrement mode.
225: * Default is IO_Increment.
226: */
227: - (IOReturn)setIncrementMode : (IOIncrementMode)mode
228: forChannel : (unsigned)localChannel
229: {
230: unsigned int realChannel;
231: int dir;
232:
233: if(mode == IO_Decrement) {
234: return IO_R_UNSUPPORTED; // nope!
235: }
236: if (localChannel >= [(IOEISADeviceDescription *)_deviceDescription numChannels])
237: return IO_R_INVALID_ARG;
238:
239: realChannel = [self _localToChannel:localChannel];
240:
241: dir = (mode == IO_Decrement ? DMA_ADRS_DECR : DMA_ADRS_INCR);
242: dma_chan_adrs_dir(realChannel, dir);
243: return IO_R_SUCCESS;
244: }
245:
246:
247: /*
248: * Returns a DMA buffer for the contents of physical memory starting at
249: * addr and continuing for length bytes. If the physical address changed
250: * to accommodate the ISA bus, the new physical address is returned in
251: * place. The DMABuffer is an opaque type.
252: *
253: * Warning: this method allocates memory, so it can block!
254: */
255: - (IOEISADMABuffer) createDMABufferFor:(unsigned int *) physAddr
256: length:(unsigned int) length
257: read:(BOOL) isRead
258: needsLowMemory:(BOOL) lowerMem
259: limitSize:(BOOL) limitSize
260: {
261: dma_xfer_t *xfer = IOMalloc(sizeof(dma_xfer_t));
262:
263: if (xfer == NULL)
264: return NULL;
265:
266: xfer->phys = *physAddr;
267: xfer->len = length;
268: xfer->read = isRead;
269: xfer->lower16 = lowerMem;
270: xfer->bound64 = limitSize;
271: xfer->buffered = FALSE;
272:
273: if (!dma_xfer(xfer, physAddr)) {
274: xpr_dmabuf("createDMABufferFor: dma_xfer() failed; physAddr"
275: "0x%x\n", physAddr, 2,3,4,5);
276: IOFree(xfer, sizeof(dma_xfer_t));
277: return NULL;
278: }
279:
280: xpr_dmabuf("createDMABufferFor: xfer 0x%x\n", xfer, 2,3,4,5);
281: return (IOEISADMABuffer) xfer;
282: }
283:
284:
285: - (void) freeDMABuffer:(IOEISADMABuffer) dmaBuffer
286: {
287: dma_xfer_t *xfer = (dma_xfer_t *) dmaBuffer;
288:
289: xpr_dmabuf("freeDMABuffer: dmaBuffer active %d 0x%x\n",
290: xfer, xfer->active,3,4,5);
291: if (xfer->active)
292: dma_xfer_done(xfer);
293: IOFree(xfer, sizeof(dma_xfer_t));
294: }
295:
296:
297: - (void) abortDMABuffer:(IOEISADMABuffer) dmaBuffer
298: {
299: dma_xfer_t *xfer = (dma_xfer_t *) dmaBuffer;
300:
301: xpr_dmabuf("abortDMABuffer: dmaBuffer active %d 0x%x\n",
302: xfer, xfer->active,3,4,5);
303: if (xfer->active)
304: dma_xfer_abort(xfer);
305: IOFree(xfer, sizeof(dma_xfer_t));
306: }
307:
308: - (IOReturn) startDMAForBuffer:(IOEISADMABuffer) buffer channel:(unsigned int) localChannel
309: {
310: unsigned int realChannel;
311:
312: if (localChannel >= [(IOEISADeviceDescription *)_deviceDescription numChannels])
313: return IO_R_INVALID_ARG;
314:
315: realChannel = [self _localToChannel:localChannel];
316:
317: if (dma_xfer_chan(realChannel, (dma_xfer_t *) buffer))
318: return IO_R_SUCCESS;
319: else
320: return IO_R_NO_FRAMES; /* XXX */
321: }
322:
323: /*
324: * Return localChannel's current address and count.
325: */
326: - (unsigned)currentAddressForChannel : (unsigned)localChannel
327: {
328: unsigned int realChannel;
329:
330: if (localChannel >= [(IOEISADeviceDescription *)_deviceDescription numChannels])
331: return 0;
332: realChannel = [self _localToChannel:localChannel];
333: return(get_dma_addr(realChannel));
334: }
335:
336: - (unsigned)currentCountForChannel : (unsigned)localChannel
337: {
338: unsigned int realChannel;
339:
340: if (localChannel >= [(IOEISADeviceDescription *)_deviceDescription numChannels])
341: return 0;
342: realChannel = [self _localToChannel:localChannel];
343: return(get_dma_count(realChannel));
344: }
345:
346:
347: - (BOOL) isDMADone:(unsigned int) localChannel
348: {
349: if (localChannel >= [(IOEISADeviceDescription *)_deviceDescription numChannels])
350: return NO;
351:
352: return (is_dma_done([self _localToChannel:localChannel]) ?
353: YES : NO);
354: }
355:
356:
357:
358:
359: /*
360: * Determine whether or not the aassociated device is connected to an EISA
361: * bus. Returns YES if so, else returns NO.
362: */
363: extern BOOL is_ISA; // in driverkit/i386/autoconf_i386.m
364:
365: - (BOOL)isEISAPresent
366: {
367: return (is_ISA ? NO : YES);
368: }
369:
370: - (BOOL)getEISAId: (unsigned int *)_id forSlot: (int) slot
371: {
372: return eisa_id(slot, _id);
373: }
374:
375: /*
376: * DMA lock package.
377: */
378:
379: typedef struct _dmaQueueEntry dmaQueueEntry;
380: struct _dmaQueueEntry {
381: id device;
382: int count;
383: dmaQueueEntry *next;
384: };
385:
386: static dmaQueueEntry *dmaQueueHead;
387: static dmaQueueEntry *dmaQueueTail;
388: static int sleepChannel; // for sleep/wakeup
389: static simple_lock_t dmaSpinLock; // ditto
390:
391: int dmaLockDisable = 0;
392:
393: extern void thread_wakeup(int x);
394:
395: /*
396: * Init routine, called out from dev_server_init().
397: */
398: void initDmaLock()
399: {
400: dmaQueueHead = NULL;
401: dmaQueueTail = NULL;
402: dmaSpinLock = simple_lock_alloc();
403: simple_lock_init(dmaSpinLock);
404: }
405:
406: /*
407: * Sleep until specified dmaQueueEntry is at the head of dmaQueue.
408: * Note we have to wait for a specific dmaQueueEntry to bubble to the head,
409: * not just any dmaQueueEntry for a specific device, since one device can have
410: * one entry at the head and another somewhere back in line.
411: *
412: * dmaSpinLock held on entry and exit.
413: */
414: static void waitTilHead(dmaQueueEntry *queueEntry)
415: {
416: while(queueEntry != dmaQueueHead) {
417: thread_sleep((int)&sleepChannel, dmaSpinLock, FALSE);
418: simple_lock(dmaSpinLock);
419: }
420: }
421:
422: /*
423: * Cons up a new dmaQueueEntry, count of 1, for specified device.
424: */
425: static inline dmaQueueEntry *queueEntryAlloc(id device)
426: {
427: dmaQueueEntry *queueEntry = IOMalloc(sizeof(dmaQueueEntry));
428:
429: queueEntry->count = 1;
430: queueEntry->device = device;
431: queueEntry->next = NULL;
432: return queueEntry;
433: }
434:
435: /*
436: * Free a dmaQueueEntry.
437: */
438: static inline void queueEntryFree(dmaQueueEntry *queueEntry)
439: {
440: ASSERT(queueEntry->count == 0);
441: IOFree(queueEntry, sizeof(dmaQueueEntry));
442: }
443:
444: /*
445: * Acquire dma lock. Allows multiple threads associated with one device
446: * instance to hold the DMA lock siimultaneously. If calling device
447: * owns the lock but other devices are blocked waiting for it, calling
448: * thread will block, and will not acquire the lock until all other pending
449: * requests are honored.
450: */
451: - (void)reserveDMALock
452: {
453: dmaQueueEntry *queueEntry;
454: dmaQueueEntry *newQueueEntry;
455:
456: if([self isEISAPresent] || dmaLockDisable) {
457: return;
458: }
459: newQueueEntry = queueEntryAlloc(self);
460: simple_lock(dmaSpinLock);
461: if(dmaQueueHead == NULL) {
462: /*
463: * Trivial quiescent case.
464: */
465: dmaQueueHead = dmaQueueTail = newQueueEntry;
466: simple_unlock(dmaSpinLock);
467: return;
468: }
469: queueEntry = dmaQueueHead;
470: if((queueEntry->device == self) && (queueEntry->next == NULL)) {
471: /*
472: * This device owns the lock, and no other requests pending.
473: */
474: queueEntry->count++;
475: simple_unlock(dmaSpinLock);
476: queueEntryFree(newQueueEntry);
477: return;
478: }
479:
480: /*
481: * See if any subsequent entries (after the first one) match this
482: * device.
483: */
484: queueEntry = queueEntry->next;
485: while(queueEntry) {
486: if(queueEntry->device == self) {
487: /*
488: * Calling device has a request pending, not at head
489: * of queue. Lump these together.
490: */
491: queueEntry->count++;
492: waitTilHead(queueEntry);
493: simple_unlock(dmaSpinLock);
494: queueEntryFree(newQueueEntry);
495: return;
496: }
497: queueEntry = queueEntry->next;
498: }
499:
500: /*
501: * New request for this device.
502: */
503: dmaQueueTail->next = newQueueEntry;
504: dmaQueueTail = newQueueEntry;
505: waitTilHead(newQueueEntry);
506: simple_unlock(dmaSpinLock);
507: return;
508: }
509:
510: - (void)releaseDMALock
511: {
512: dmaQueueEntry *queueEntry = dmaQueueHead;
513: dmaQueueEntry *freeQueueEntry = NULL;
514: boolean_t needWakeup = FALSE;
515:
516: if([self isEISAPresent] || dmaLockDisable) {
517: return;
518: }
519: simple_lock(dmaSpinLock);
520: if(queueEntry->device != self) {
521: simple_unlock(dmaSpinLock);
522: IOLog("%s: releaseDmaLock when not holding lock\n",
523: [self name]);
524: panic("releaseDMALock");
525: }
526: if(--queueEntry->count == 0) {
527: /*
528: * This device is done. Wake up others, if any.
529: */
530: needWakeup = (queueEntry->next != NULL);
531: freeQueueEntry = queueEntry;
532: dmaQueueHead = queueEntry->next;
533: }
534: simple_unlock(dmaSpinLock);
535: if(needWakeup) {
536: thread_wakeup((int)&sleepChannel);
537: }
538: if(freeQueueEntry != NULL) {
539: queueEntryFree(freeQueueEntry);
540: }
541: }
542:
543: /*
544: * Support for the extended mode register (EISA only).
545: */
546:
547: - (IOReturn)setDMATransferWidth : (IOEISADMATransferWidth)width
548: forChannel : (unsigned)localChannel
549: {
550: unsigned int realChannel;
551: int dmaWidth;
552:
553: if((width == IO_16BitWordCount) || is_ISA) {
554: return IO_R_UNSUPPORTED;
555: }
556: if (localChannel >= [[self deviceDescription] numChannels]) {
557: return IO_R_INVALID_ARG;
558: }
559: realChannel = [self _localToChannel:localChannel];
560:
561: switch(width) {
562: case IO_8Bit:
563: dmaWidth = DMA_XFR_8_BIT;
564: break;
565: case IO_16BitWordCount:
566: dmaWidth = DMA_XFR_16_BIT_WORD;
567: break;
568: case IO_16BitByteCount:
569: dmaWidth = DMA_XFR_16_BIT_BYTE;
570: break;
571: case IO_32Bit:
572: dmaWidth = DMA_XFR_32_BIT;
573: break;
574: default:
575: return IO_R_INVALID_ARG;
576: }
577: dma_xfer_width(realChannel, dmaWidth);
578: return IO_R_SUCCESS;
579: }
580:
581: - (IOReturn)getDMATransferWidth : (IOEISADMATransferWidth *)width_p
582: forChannel : (unsigned)localChannel
583: {
584: unsigned int realChannel;
585: int width;
586:
587: if (localChannel >= [[self deviceDescription] numChannels]) {
588: return IO_R_INVALID_ARG;
589: }
590: realChannel = [self _localToChannel:localChannel];
591:
592: width = get_dma_xfer_width(realChannel);
593: switch(width) {
594: case DMA_XFR_8_BIT:
595: *width_p = IO_8Bit;
596: break;
597: case DMA_XFR_16_BIT_WORD:
598: *width_p = IO_16BitWordCount;
599: break;
600: case DMA_XFR_16_BIT_BYTE:
601: *width_p = IO_16BitByteCount;
602: break;
603: case DMA_XFR_32_BIT:
604: *width_p = IO_32Bit;
605: break;
606: default: /* should never be reached */
607: return IO_R_NOT_ATTACHED;
608: }
609: return IO_R_SUCCESS;
610: }
611:
612: /*
613: * Select DMA Timing.
614: */
615: - (IOReturn)setDMATiming : (IOEISADMATiming)timing
616: forChannel : (unsigned)localChannel
617: {
618: unsigned int realChannel;
619: int dmaTiming;
620:
621: if(is_ISA) {
622: return IO_R_UNSUPPORTED;
623: }
624: if (localChannel >= [[self deviceDescription] numChannels]) {
625: return IO_R_INVALID_ARG;
626: }
627: realChannel = [self _localToChannel:localChannel];
628:
629: switch(timing) {
630: case IO_Compatible:
631: dmaTiming = DMA_TIMING_COMPAT;
632: break;
633: case IO_TypeA:
634: dmaTiming = DMA_TIMING_A;
635: break;
636: case IO_TypeB:
637: dmaTiming = DMA_TIMING_B;
638: break;
639: case IO_Burst:
640: dmaTiming = DMA_TIMING_BURST;
641: break;
642: }
643: dma_timing(realChannel, dmaTiming);
644: return IO_R_SUCCESS;
645: }
646:
647: /*
648: * Select whether EOP pin is output (default) or input.
649: */
650: - (IOReturn)setEOPAsOutput : (BOOL)flag
651: forChannel : (unsigned)localChannel
652: {
653: unsigned realChannel;
654: unsigned dmaFlag;
655:
656: if(is_ISA) {
657: return IO_R_UNSUPPORTED;
658: }
659: if (localChannel >= [[self deviceDescription] numChannels]) {
660: return IO_R_INVALID_ARG;
661: }
662: realChannel = [self _localToChannel:localChannel];
663:
664: dmaFlag = (flag ? DMA_EOP_OUT : DMA_EOP_IN);
665: dma_eop_in(realChannel, dmaFlag);
666: return IO_R_SUCCESS;
667: }
668:
669: /*
670: * Enable Stop register. Default is disabled; enabling this feature
671: * is not supported in [PR2]the current release.
672: */
673: - (IOReturn)setStopRegisterMode : (IOEISAStopRegisterMode)mode
674: forChannel : (unsigned)localChannel
675: {
676: unsigned realChannel;
677: unsigned dmaFlag;
678:
679: if((mode == IO_StopRegisterEnable) || is_ISA) {
680: return IO_R_UNSUPPORTED;
681: }
682: if (localChannel >= [[self deviceDescription] numChannels]) {
683: return IO_R_INVALID_ARG;
684: }
685: realChannel = [self _localToChannel:localChannel];
686:
687: if(mode == IO_StopRegisterEnable) {
688: dmaFlag = DMA_STOP_ENABLE;
689: }
690: else {
691: dmaFlag = DMA_STOP_DISABLE;
692: }
693: dma_stop_enable(realChannel, dmaFlag);
694: return IO_R_SUCCESS;
695: }
696:
697: - (IOReturn) reservePortRange : (unsigned int) localPortRange
698: {
699: return IO_R_SUCCESS;
700: }
701:
702: - (void) releasePortRange : (unsigned int) localPortRange
703: {
704: }
705:
706: @end
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.