VoodooSDHCI

VoodooSDHCI Svn Source Tree

Root/trunk/VoodooSDHC.cpp

  • Property svn:executable set to *
1#include "License.h"
2/*****************************************************************************/
3/* Build Configuration Defines */
4
5/* Debug - mostly turns on more logging */
6//#define __DEBUG__1
7
8/*
9 * Builds the driver to prohibit writes to the card. Useful while building
10 * confidence in changes without corrupting data.
11 */
12//#define READONLY_DRIVER1
13
14/*
15 * Builds the driver with High Speed Card support.
16 */
17//#define HIGHSPEED_CARD_MODE1
18
19/*
20 * Builds the driver with 4-bit Bus support.
21 */
22#define WIDE_BUS_MODE1
23
24/*
25 * Use multiblock reads/writes. Define to either 0 or 1
26 */
27#define USE_MULTIBLOCK1
28
29/*
30 * Use SDMA reads/writes. Define to either 0 or 1
31 */
32#define USE_SDMA 1
33
34/*
35 * The Linux driver for this device claimed that the card needs to be reset
36 * after every command. That doesn't seem to be necessary so we turn on
37 * NO_RESET_WAR.
38 */
39#define NO_RESET_WAR1
40
41#define SDMA_BUFFER_SIZE 32768
42#define SDMA_BUFFER_SIZE_IN_REG 0x3000 /* see def. of Block Size Register */
43#define SDMA_RETRY_COUNT 5
44
45
46/*****************************************************************************/
47//#include <libkern/OSByteOrder.h>
48
49#include "VoodooSDHC.h"
50#include "SDHCI_Register_Map.h"
51#include "SD_Commands.h"
52#include "sdhci.h"
53
54#define MAX(a, b)((a) > (b) ? (a) : (b))
55#define MIN(a, b)((a) < (b) ? (a) : (b))
56
57#definesuper IOBlockStorageDevice
58
59OSDefineMetaClassAndStructors ( VoodooSDHC, IOBlockStorageDevice );
60
61/*****************************************************************************/
62/* Helper Functions */
63/*
64 * read_block_pio: Read a single 512 byte block of data with no error
65 * checking from a PIO address to memory.
66 *volatile UInt32 *reg_addr: Address of card's PIO register
67 *UInt8 *buf: Buffer in which to place data - m
68 */
69static inline void read_block_pio(volatile UInt32 *reg_addr, UInt32 *buf) {
70UInt32 *bufp = buf;
71
72for (int i = 0; i < 512 / sizeof(UInt32); i++) {
73*bufp++ = *reg_addr;
74}
75}
76
77/*****************************************************************************/
78/* Main Driver Code */
79
80/*
81 * init: Initialize driver. Returns true on success, false on failure
82 *OSDictionary *properties: Driver properties passed in
83 */
84bool VoodooSDHC::init ( OSDictionary * properties )
85{
86IOLog("VoodooSDHCI ::: an SDHCI driver for Ricoh, TI, and JMicron SD Host Controllers ::: rev 20091008\n");
87IOLog("VoodooSDHCI: initializing SD host controller\n");
88/* Run by our superclass */
89if ( super::init ( properties ) == false )
90{
91return false;
92}
93
94#ifdef USE_SDMA
95if ((workLoop = IOWorkLoop::workLoop()) == NULL)
96return false;
97#endif
98
99return true;
100}
101
102void VoodooSDHC::free()
103{
104#ifdef USE_SDMA
105if (workLoop != NULL) {
106workLoop->release();
107workLoop = NULL;
108}
109#endif
110super::free();
111}
112
113/*
114 * start: Starts driver. Called upon insertion of card. Returns true
115 * on success, false on failure.
116 *IOService *provider: Provider structure
117 */
118bool VoodooSDHC::start ( IOService * provider )
119{
120#ifdef __DEBUG__
121IOLog("VoodooSDHCI: running start()\n");
122IOLog("VoodooSDHCI: we have found %d SD Host Controllers\n",provider->getDeviceMemoryCount());
123#endif
124super::start ( provider );
125lock.init();
126#ifdef USE_SDMA
127sdmaCond = IOLockAlloc();
128mediaStateLock = IOLockAlloc();
129#endif
130#ifdef __DEBUG__
131IOLog("VoodooSDHCI: starting card power management\n");
132#endif
133// initialize superclass variables from IOService.h
134PMinit();
135static const IOPMPowerState powerStates[] = {
136{kIOPMPowerStateVersion1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
137{kIOPMPowerStateVersion1, kIOPMDeviceUsable, IOPMPowerOn, IOPMPowerOn, 0, 0, 0, 0, 0, 0, 0, 0}
138};
139registerPowerDriver(this, const_cast<IOPMPowerState*>(powerStates),
140sizeof(powerStates) / sizeof(powerStates[0]));
141// join the tree from IOService.h
142provider->joinPMtree(this);
143
144#ifdef USE_SDMA
145sdmaBuffDesc = IOBufferMemoryDescriptor::withCapacity(SDMA_BUFFER_SIZE * 2, kIODirectionInOut, true);
146physSdmaBuff = sdmaBuffDesc->getPhysicalAddress();
147virtSdmaBuff = (char*)sdmaBuffDesc->getBytesNoCopy() + SDMA_BUFFER_SIZE - physSdmaBuff % SDMA_BUFFER_SIZE;
148physSdmaBuff += SDMA_BUFFER_SIZE - physSdmaBuff % SDMA_BUFFER_SIZE;
149#endif
150
151cardPresence = kCardNotPresent;
152if (! setup(provider)) {
153return false;
154}
155
156#ifdef USE_SDMA
157IOWorkLoop *workLoop;
158if ((workLoop = getWorkLoop()) == NULL) {
159IOLog("VoodooSDHCI: unable to get a workloop; getWorkLoop() == NULL\n");
160return false;
161}
162if ((interruptSrc = IOFilterInterruptEventSource::filterInterruptEventSource(
163this, interruptHandler, interruptFilter, provider
164)) == NULL) {
165IOLog("VoodooSDHCI: failed to create an interrupt source\n");
166return false;
167}
168if (workLoop->addEventSource(interruptSrc) != kIOReturnSuccess) {
169IOLog("VoodooSDHCI: failed to add FIES to work loop\n");
170return false;
171}
172if ((timerSrc = IOTimerEventSource::timerEventSource(this, timerHandler)) == NULL) {
173IOLog("VoodooSDHCI: failed to create a timer event source\n");
174return false;
175}
176if (workLoop->addEventSource(timerSrc) != kIOReturnSuccess) {
177IOLog("VoodooSDHCI: failed ot add TES to work loop\n");
178return false;
179}
180#endif
181
182this->attach(this);
183registerService();
184
185#ifdef USE_SDMA
186interruptSrc->enable();
187timerSrc->enable();
188timerSrc->setTimeoutMS(50); // intial timeout is small, to detect card insertion ASAP
189#endif
190
191// The controller is now initialized and ready for operation
192return true;
193}
194
195/*
196 * setup: Initializes I/O, called upon start and resume, returns true on success.
197 *IOService *provider: Provider structure
198 */
199bool VoodooSDHC::setup(IOService * provider)
200{
201IODeviceMemory *pMem;
202UInt8 slot = 0;
203
204for (slot=0; slot<provider->getDeviceMemoryCount(); slot++) {
205pMem = provider->getDeviceMemoryWithIndex(0);
206this->PCIRegMap = provider->mapDeviceMemoryWithIndex(0);
207if (!this->PCIRegMap) {
208IOLog("VoodooSDHCI: PCI Register Mapping for Device %d Failed!\n", (int)slot);
209return false;
210}
211
212this->PCIRegP[slot] =
213(SDHCIRegMap_t *)PCIRegMap->getVirtualAddress();
214#ifdef __DEBUG__
215IOLog("VoodooSDHCI: controller slot == %d\n", slot);
216IOLog("VoodooSDHCI: unit memory (pMem) == %d\n", pMem->getLength());
217#endif
218this->PCIRegP[slot]->PowerControl = 0;
219Reset(slot, FULL_RESET);
220IODelay(10000);
221if (cardPresence == kCardIsPresent && isCardPresent(slot)) {
222SDCIDReg_t oldCID = SDCIDReg[slot];
223cardInit(slot);
224if (memcmp(&oldCID, SDCIDReg + slot, sizeof(oldCID)) != 0) {
225IOLog("VoodooSDHCI: oops! we found a different card :: remount?\n");
226cardPresence = kCardRemount;
227}
228}
229}
230
231return true;
232}
233
234/*
235 * stop: Called on completion of service, such as ejection of card's
236 * filesystem.
237 *IOService *provider: Our provider structure
238 */
239void VoodooSDHC::stop(IOService *provider)
240{
241#ifdef __DEBUG__
242IOLog("VoodooSDHCI: card is in stop() function\n");
243#endif
244#ifdef USE_SDMA
245if (timerSrc != NULL) {
246timerSrc->disable();
247timerSrc->cancelTimeout();
248getWorkLoop()->removeEventSource(timerSrc);
249timerSrc->release();
250timerSrc = NULL;
251}
252if (interruptSrc != NULL) {
253interruptSrc->disable();
254getWorkLoop()->removeEventSource(interruptSrc);
255interruptSrc->release();
256interruptSrc = NULL;
257}
258sdmaBuffDesc->release();
259#endif
260
261PMstop();
262#ifdef USE_SDMA
263IOLockFree(sdmaCond);
264IOLockFree(mediaStateLock);
265#endif
266lock.free();
267
268// Call our superclass
269super::stop ( provider );
270
271}
272/*
273 * dumpRegs: Dump selected Host registers. Only used for driver
274 * debugging.
275 *UInt8 slot: Which card slot to dump
276 */
277void VoodooSDHC::dumpRegs(UInt8 slot) {
278IOLog("VoodooSDHCI: Register Dump ******************************************************\n");
279IOLog("VoodooSDHCI: SDMASysAddr: 0x%08X PresentState: 0x%08X\n",
280this->PCIRegP[slot]->SDMASysAddr, this->PCIRegP[slot]->PresentState);
281IOLog("VoodooSDHCI: BlockSize: 0x%04X BlockCount: 0x%04X\n",
282this->PCIRegP[slot]->BlockSize, this->PCIRegP[slot]->BlockCount);
283IOLog("VoodooSDHCI: TransferMode: 0x%04X Command: 0x%04X\n",
284this->PCIRegP[slot]->TransferMode, this->PCIRegP[slot]->Command);
285IOLog("VoodooSDHCI: HostControl: 0x%02X PowerControl: 0x%02X\n",
286this->PCIRegP[slot]->HostControl, this->PCIRegP[slot]->PowerControl);
287IOLog("VoodooSDHCI: BlockGapControl: 0x%02X WakeupControl: 0x%02X\n",
288this->PCIRegP[slot]->BlockGapControl, this->PCIRegP[slot]->WakeupControl);
289IOLog("VoodooSDHCI: ClockControl: 0x%04X TimeoutControl: 0x%02X\n",
290this->PCIRegP[slot]->ClockControl, this->PCIRegP[slot]->TimeoutControl);
291IOLog("VoodooSDHCI: SoftwareReset: 0x%02X NormalIntStatus: 0x%04X\n",
292this->PCIRegP[slot]->SoftwareReset, this->PCIRegP[slot]->NormalIntStatus);
293IOLog("VoodooSDHCI: ErrorIntStatus: 0x%04X NormalIntStatusEn: 0x%04X\n",
294this->PCIRegP[slot]->ErrorIntStatus, this->PCIRegP[slot]->NormalIntStatusEn);
295IOLog("VoodooSDHCI: ErrorIntStatusEn: 0x%04X NormalIntSignalEn: 0x%04X\n",
296this->PCIRegP[slot]->ErrorIntStatusEn, this->PCIRegP[slot]->NormalIntSignalEn);
297IOLog("VoodooSDHCI: ErrorIntSignalEn: 0x%04X CMD12ErrorStatus: 0x%04X\n",
298this->PCIRegP[slot]->ErrorIntSignalEn, this->PCIRegP[slot]->CMD12ErrorStatus);
299IOLog("VoodooSDHCI: Capabilities[1]: 0x%08X Capabilities[0]: 0x%08X\n",
300this->PCIRegP[slot]->Capabilities[1], this->PCIRegP[slot]->Capabilities[0]);
301IOLog("VoodooSDHCI: MaxCurrentCap[1]: 0x%08X MaxCurrentCap[0]: 0x%08X\n",
302this->PCIRegP[slot]->MaxCurrentCap[1], this->PCIRegP[slot]->MaxCurrentCap[0]);
303IOLog("VoodooSDHCI: ForceEventCMD12ErrStatus: 0x%04X ForceEventErrorIntStatus: 0x%04X\n",
304this->PCIRegP[slot]->ForceEventCMD12ErrStatus, this->PCIRegP[slot]->ForceEventErrorIntStatus);
305IOLog("VoodooSDHCI: AMDAErrorStatus: 0x%02X Argument 0x%08X\n",
306this->PCIRegP[slot]->AMDAErrorStatus, PCIRegP[slot]->Argument);
307IOLog("VoodooSDHCI: ADMASystemAddr[1]: 0x%08X ADMASystemAddr[0]: 0x%08lX\n",
308this->PCIRegP[slot]->ADMASystemAddr[1], this->PCIRegP[slot]->ADMASystemAddr[0]);
309IOLog("VoodooSDHCI: SlotIntStatus: 0x%04X HostControllerVer: 0x%04X\n",
310this->PCIRegP[slot]->SlotIntStatus, this->PCIRegP[slot]->HostControllerVer);
311IOLog("VoodooSDHCI: Response[1]: 0x%08X Response[0]: 0x%08X\n",
312this->PCIRegP[slot]->Response[1], this->PCIRegP[slot]->Response[0]);
313IOLog("VoodooSDHCI: Response[3]: 0x%08X Response[2]: 0x%08X\n",
314this->PCIRegP[slot]->Response[3], this->PCIRegP[slot]->Response[2]);
315IOLog("VoodooSDHCI: End of Register Dump************************************************\n");
316}
317
318/*
319 * cardInit: Initialize a card. Called upon insertion. When complete,
320 * card should be running at full speed and card data
321 * populated.
322 *UInt8 slot: Which slot the card is in.
323 */
324bool VoodooSDHC::cardInit(UInt8 slot)
325{
326isHighCapacity = false;
327calcClock(slot, 400000);
328powerSD(slot);
329SDCommand(slot, SD_GO_IDLE_STATE, SDCR0, 0);
330IODelay(30000);
331SDCommand(slot, SD_SEND_IF_COND, SDCR8, 0x000001AA);
332for (int i = 0; i < 100; i++) {
333IODelay(10000);
334if (!(PCIRegP[slot]->PresentState & ComInhibitCMD)) {
335break;
336}
337}
338
339if(this->PCIRegP[slot]->PresentState & ComInhibitCMD) {
340IOLog("VoodooSDHCI: no response from CMD_8 -- ComInhibitCMD\n");
341Reset(slot, CMD_RESET);
342Reset(slot, DAT_RESET);
343SDCommand(slot, SD_GO_IDLE_STATE, SDCR0, 0);
344do {
345SDCommand(slot, SD_APP_CMD, SDCR55, 0);
346SDCommand(slot, SD_APP_OP_COND, SDACR41, 0x00FF8000);
347IODelay(1000);
348} while (!(this->PCIRegP[slot]->Response[0] & BIT31));
349} else {
350// check and init SDHC (wait for 2 secs; spec requires 1 sec)
351IOLog("VoodooSDHCI: initializing spec 2.0 SD card\n");
352for (int i = 0; i < 80; i++) {
353#ifdef __DEBUG__
354IOLog("VoodooSDHCI: sending CMD_55\n");
355#endif //me
356SDCommand(slot, SD_APP_CMD, SDCR55, 0);
357#ifdef __DEBUG__
358IOLog("VoodooSDHCI: sending APP_CMD_41\n");
359#endif //me
360SDCommand(slot, SD_APP_OP_COND, SDACR41, 0x40FF8000);
361IODelay(25000);
362if (this->PCIRegP[slot]->Response[0] & BIT31) {
363goto OP_COND_COMPLETE;
364}
365}
366#ifdef __DEBUG__
367IOLog("VoodooSDHCI: no response to APP_CMD_41: 0x%08x\n", PCIRegP[slot]->Response[0]);
368#endif
369return false;
370OP_COND_COMPLETE:
371#ifdef __DEBUG__
372IOLog("VoodooSDHCI: got response to APP_CMD_41: 0x%08x\n", PCIRegP[slot]->Response[0]);
373#endif
374if (this->PCIRegP[slot]->Response[0] & BIT30) {
375IOLog("VoodooSDHCI: we have HC card\n");
376isHighCapacity = true;
377} else {
378IOLog("VoodooSDHCI: standard SD (without HC)\n");
379}
380}
381
382
383SDCommand(slot, SD_ALL_SEND_CID, SDCR2, 0);
384IODelay(1000);
385parseCID(slot);
386SDCommand(slot, SD_SET_RELATIVE_ADDR, SDCR3, 0);
387IODelay(1000);
388calcClock(slot, 25000000);
389this->RCA = this->PCIRegP[slot]->Response[0] >> 16;
390#ifdef __DEBUG__
391IOLog("VoodooSDHCI: RCA == 0x%08X\n", this->RCA);
392#endif//me
393SDCommand(slot, SD_SEND_CSD, SDCR9, this->RCA << 16);
394IODelay(2000000);
395parseCSD(slot);
396#ifdef __DEBUG__
397IOLog("VoodooSDHCI: PCIRegP response order (3,2,1,0) :: 0x%08X 0x%08X 0x%08X 0x%08X\n",
398this->PCIRegP[slot]->Response[3],
399this->PCIRegP[slot]->Response[2],
400this->PCIRegP[slot]->Response[1],
401this->PCIRegP[slot]->Response[0]);
402#endif//me
403SDCommand(slot, SD_SELECT_CARD, SDCR7, this->RCA << 16);
404IODelay(10000);
405
406#ifdef WIDE_BUS_MODE
407/* XXX - Need to check whether the card is capable before enabiling this */
408#ifdef __DEBUG__
409IOLog("VoodooSDHCI: WIDE_BUS_MODE :: setting 4 bit mode\n");
410#endif //me
411SDCommand(0, SD_APP_CMD, SDCR55, this->RCA << 16);
412SDCommand(slot, SD_APP_SET_BUS_WIDTH, SDCR6, 2);
413IODelay(30000);
414#ifdef__DEBUG__
415IOLog("VoodooSDHCI: PCIRegP response order (3,2,1,0) :: 0x%08X 0x%08X 0x%08X 0x%08X\n",
416this->PCIRegP[slot]->Response[3],
417this->PCIRegP[slot]->Response[2],
418this->PCIRegP[slot]->Response[1],
419this->PCIRegP[slot]->Response[0]);
420#endif//me
421if (!(this->PCIRegP[slot]->Response[0] & 0x480000)) { /* check ERROR and ILLEGAL COMMAND */
422this->PCIRegP[slot]->HostControl |= SDHCI_CTRL_4BITBUS;
423#ifdef __DEBUG__
424IOLog("VoodooSDHCI: properly switched to 4 bit mode\n");
425#endif//me
426} else {
427#ifdef __DEBUG__
428IOLog("VoodooSDHCI: unable to switch to 4 bit mode -- calling Reset(slot, {CMD,DAT}_RESET)\n");
429#endif//me
430Reset(slot, CMD_RESET);
431Reset(slot, DAT_RESET);
432}
433IODelay(30000);
434#endif /* WIDE_BUS_MODE */
435#ifdef HIGHSPEED_CARD_MODE
436/* XXX - Need to check whether the card is capable before enabiling this */
437#ifdef __DEBUG__
438IOLog("VoodooSDHCI: HIGHSPEED_CARD_MODE \n");
439#endif //me
440SDCommand(slot, SD_SWITCH, SDCR6, 0x01fffff1);
441IODelay(10000);
442calcClock(slot, 50000000);
443this->PCIRegP[slot]->HostControl |= SDHCI_CTRL_HISPD;
444#endif /* HIGHSPEED_CARD_MODE */
445
446this->PCIRegP[slot]->BlockSize = 512;
447this->PCIRegP[slot]->BlockCount = 1;
448#ifdef __DEBUG__
449IOLog("VoodooSDHCI: Card Init: Host Control = 0x%x\n", this->PCIRegP[slot]->HostControl);
450#endif
451this->PCIRegP[slot]->HostControl |= 0x1;
452#ifdef __DEBUG__
453IOLog("VoodooSDHCI: Card Init: Host Control = 0x%x\n", this->PCIRegP[slot]->HostControl);
454#endif
455return true;
456}
457
458/*
459 * isCardPresent: Return true if card is present, false otherwise
460 *UInt8 slot: Which slot the card is in
461 */
462bool VoodooSDHC::isCardPresent(UInt8 slot) {
463return PCIRegP[slot]->PresentState & CardInserted;
464}
465
466/*
467 * isCardWP: Return true if card is write protected, false otherwise
468 *UInt8 slot: Which slot the card is in.
469 */
470bool VoodooSDHC::isCardWP(UInt8 slot) {
471#ifndef READONLY_DRIVER
472return !(this->PCIRegP[slot]->PresentState & WPSwitchLevel);
473#else
474return true;
475#endif
476}
477
478IOReturn VoodooSDHC::setPowerState ( unsigned long state, IOService * provider )
479// Note that it is safe to ignore the whatDevice parameter.
480{
481switch (state) {
482case 0: // sleep
483#ifdef __DEBUG__
484IOLog("VoodooSDHCI: sleep requested by thread: 0x%08x\n", (int)IOThreadSelf());
485#endif //me
486lock.lock();
487break;
488case 1: // wakeup
489#ifdef __DEBUG__
490IOLog("VoodooSDHCI: wakeup requested by thread: 0x%08x\n", (int)IOThreadSelf());
491#endif //me
492setup(getProvider());
493lock.unlock();
494break;
495}
496return kIOPMAckImplied;
497}
498
499/*
500 * LEDControl: Turns on/off LED on card slot. Not present on Dell Mini 9.
501 *UInt8 slot: Which slot the card is in.
502 *bool state: True for on, false for off
503 */
504void VoodooSDHC::LEDControl(UInt8 slot, bool state) {
505if (state) {
506this->PCIRegP[slot]->HostControl |= LedControl;
507} else {
508this->PCIRegP[slot]->HostControl &= ~LedControl;
509}
510}
511
512/*
513 * Reset: Reset SDHCI host controller. Reset levels are defined by SDHCI
514 * standard.
515 *UInt8 slot: Which host controller to reset
516 *UInt8 type: Reset type (Command, Data, or Full)
517 */
518void VoodooSDHC::Reset(UInt8 slot, UInt8 type)
519{
520switch(type) {
521case CMD_RESET:
522this->PCIRegP[slot]->SoftwareReset = CMD_RESET;
523break;
524case DAT_RESET:
525this->PCIRegP[slot]->SoftwareReset = DAT_RESET;
526break;
527default:
528this->PCIRegP[slot]->SoftwareReset = FULL_RESET;
529break;
530}
531while(this->PCIRegP[slot]->SoftwareReset);
532}
533
534/*
535 * SDCommand: Send a single command to the SDHCI Host controller. Return true on
536 * success, false on failure. Will spin wait indefinitely if device is
537 * busy.
538 *UInt8 slot: Which slot the card to send to is in
539 *UInt8 command: SDHC command as defined in SDHC Physical Interface
540 *UInt16 response: Response type to expect for command passed in
541 *UInt32 arg: Command argument as defined in SDHC Physical Interface
542 */
543bool VoodooSDHC::SDCommand(UInt8 slot, UInt8 command, UInt16 response,
544UInt32 arg) {
545if (command != 0) {
546while(this->PCIRegP[slot]->PresentState & ComInhibitCMD);
547}
548
549//if(command 1= COMMANDS?)
550//{
551//Wait for DAT to free up
552//while(this->PCIRegP[slot]->PresentState & ComInhibitDAT);
553//}
554
555switch(response) { //See SD Host Controller Spec Version 2.00 Page 30
556case R0:
557response = 0;
558break;
559case R1:
560response = BIT4|BIT3|BIT1;
561break;
562case R1b:
563response = BIT4|BIT3|BIT1|BIT0;
564break;
565case R2:
566response = BIT3|BIT0;
567break;
568case R3:
569response = BIT1;
570break;
571case R4:
572response = BIT1;
573break;
574case R5:
575response = BIT4|BIT3|BIT1;
576break;
577case R5b:
578response = BIT4|BIT3|BIT1|BIT0;
579break;
580case R6:
581response = BIT4|BIT3|BIT1;
582break;
583case R7:
584response = BIT4|BIT3|BIT1;
585break;
586}
587this->PCIRegP[slot]->Argument = arg;
588
589if (command == 17 || command == 24)
590response |= BIT5;
591
592if (command == SD_READ_MULTIPLE_BLOCK) {
593response |= BIT5;
594this->PCIRegP[0]->TransferMode =
595SDHCI_TRNS_READ | SDHCI_TRNS_MULTI |
596SDHCI_TRNS_BLK_CNT_EN | SDHCI_TRNS_ACMD12
597#ifdef USE_SDMA
598| SDHCI_TRNS_DMA
599#endif
600;
601}
602
603if (command == SD_WRITE_MULTIPLE_BLOCK) {
604response |= BIT5;
605this->PCIRegP[0]->TransferMode = SDHCI_TRNS_MULTI |
606SDHCI_TRNS_BLK_CNT_EN | SDHCI_TRNS_ACMD12
607#ifdef USE_SDMA
608| SDHCI_TRNS_DMA
609#endif
610;
611}
612
613this->PCIRegP[slot]->Command = (command << 8) | response;
614
615//IOLog("Command: %d", (command << 8) | response);
616return true;
617}
618
619/*
620 * calcClock: Calculate card clock rate. See SDHCI Host Controller spec
621 * for details on calculation. Must be called after cardInit.
622 *UInt8 slot: Host controller/slot number
623 *UInt32 clockspeed: Maximum desired clock speed
624 */
625bool VoodooSDHC::calcClock(UInt8 slot, UInt32 clockspeed) {
626UInt32 baseClock;
627UInt32 div;
628
629this->PCIRegP[slot]->ClockControl = 0;
630this->PCIRegP[slot]->ClockControl = 0;
631this->PCIRegP[slot]->ClockControl |= BIT0;
632while(!this->PCIRegP[slot]->ClockControl & BIT1);
633baseClock = ((this->PCIRegP[slot]->Capabilities[0] & 0x3F00) >> 8);
634baseClock *= 1000000;
635
636#ifdef __DEBUG__
637IOLog("VoodooSDHCI: BaseClock :: %dMHz\n", baseClock/1000000);
638#endif //me
639
640for(div=1;(baseClock / div) > clockspeed;div <<= 1);
641
642#ifdef __DEBUG__
643IOLog("VoodooSDHCI: SD Clock :: %dKHz\n", (baseClock/div)/1000);
644#endif //me
645
646div = (div<<7) & 0xFF000;
647this->PCIRegP[slot]->ClockControl |= div;
648this->PCIRegP[slot]->ClockControl |= BIT2;
649return true;
650}
651
652/*
653 * powerSD: Turn on power to SD Card. Must pay attention to voltage
654 * level supported. This is determined from the Host capability
655 * register.
656 *UInt8 slot: Slot the card is in.
657 */
658bool VoodooSDHC::powerSD(UInt8 slot) {
659this->PCIRegP[slot]->PowerControl = 0;
660#ifdef __DEBUG__
661IOLog("VoodooSDHCI: in power_sd(slot) function :: 0x%x\n", this->PCIRegP[slot]->Capabilities[0]);
662#endif
663if(this->PCIRegP[slot]->Capabilities[0] & CR3v3Support) {
664this->PCIRegP[slot]->PowerControl |= HC3v3;
665} else if(this->PCIRegP[slot]->Capabilities[0] & CR3v0Support) {
666this->PCIRegP[slot]->PowerControl |= HC3v0;
667} else if(this->PCIRegP[slot]->Capabilities[0] & CR1v8Support) {
668this->PCIRegP[slot]->PowerControl |= HC1v8;
669}
670this->PCIRegP[slot]->PowerControl |= SDPower;
671
672return true;
673}
674
675/*
676 * parseCID: Parse Card Idenitification information
677 *UInt8 slot: slot the card is in
678 */
679void VoodooSDHC::parseCID(UInt8 slot) {
680this->SDCIDReg[slot].MID = (UInt8)(this->PCIRegP[slot]->Response[3] & 0xFF000000) >> 24;
681this->SDCIDReg[slot].OID = (UInt16)(this->PCIRegP[slot]->Response[3] & 0xFFFF00) >> 8;
682this->SDCIDReg[slot].PNM[0] = (UInt8)(this->PCIRegP[slot]->Response[3] & 0xFF);
683this->SDCIDReg[slot].PNM[1] = (UInt8)((this->PCIRegP[slot]->Response[2] & 0xFF000000) >> 24);
684this->SDCIDReg[slot].PNM[2] = (UInt8)((this->PCIRegP[slot]->Response[2] & 0xFF0000) >> 16);
685this->SDCIDReg[slot].PNM[3] = (UInt8)((this->PCIRegP[slot]->Response[2] & 0xFF00) >> 8);
686this->SDCIDReg[slot].PNM[4] = (UInt8)(this->PCIRegP[slot]->Response[2] & 0xFF);
687this->SDCIDReg[slot].PNM[5] = 0;
688this->SDCIDReg[slot].PRV[0] = (UInt8)(this->PCIRegP[slot]->Response[1] & 0xF0000000) >> 28;
689this->SDCIDReg[slot].PRV[1] = (UInt8)(this->PCIRegP[slot]->Response[1] & 0xF000000) >> 24;
690this->SDCIDReg[slot].PSN = (this->PCIRegP[slot]->Response[1] & 0xFFFFFF) << 8;
691this->SDCIDReg[slot].PSN |= (this->PCIRegP[slot]->Response[0] & 0xFF000000) >> 24;
692this->SDCIDReg[slot].MDT[0] = (UInt8)(this->PCIRegP[slot]->Response[0] & 0xFF000) >> 12;
693this->SDCIDReg[slot].MDT[1] = (UInt8)(this->PCIRegP[slot]->Response[1] & 0xF00) >> 8;
694}
695
696/*
697 * parseCSD: Parse CSD information
698 *UInt8 slot: slot the card is in
699 */
700void VoodooSDHC::parseCSD(UInt8 slot) {
701switch ((UInt8)((this->PCIRegP[slot]->Response[3] & 0xC00000) >> 22)) {
702case 0: // version 1
703{
704UInt8 blLen = (UInt8)((PCIRegP[slot]->Response[2] & 0xF00) >> 8);
705UInt16 cSize = (UInt16)((PCIRegP[slot]->Response[2] & 0x3) << 10);
706cSize |= (UInt16)((PCIRegP[slot]->Response[1] & 0xFFC00000) >> 22);
707UInt8 cSizeMult = (UInt8)((PCIRegP[slot]->Response[1] & 0x000380) >> 7);
708int large_to_small = (1 << (blLen)) / 512;
709maxBlock = (cSize+1) * large_to_small *
710(1 << (cSizeMult+2)) - 1;
711}
712break;
713case 1: // version 2
714{
715int units = (UInt16)(PCIRegP[slot]->Response[1] >> 24);
716units |= (PCIRegP[slot]->Response[1] & 0x00ff0000) >> 8;
717units |= (PCIRegP[slot]->Response[2] & 0x3f) << 16;
718maxBlock = (units + 1) * 1024;
719}
720break;
721default:
722// donno how to bail out...
723;
724}
725}
726
727/*
728 * reportRemovability: Apple API function. An SD Card is a removeable
729 * device. Returns an I/O success.
730 *bool *isRemovable: Passed back. Always return true.
731 */
732IOReturn VoodooSDHC::reportRemovability(bool *isRemovable) {
733*isRemovable = true;
734#ifdef __DEBUG__
735IOLog("VoodooSDHCI: reportRemovability\n");
736#endif
737return kIOReturnSuccess;
738}
739
740/*
741 * reportWriteProtection: Apple API function. Returns whether the card is
742 *write protected. Returns an I/O success.
743 *bool *isWriteProtected: Passed back. Always return true.
744 */
745IOReturn VoodooSDHC::reportWriteProtection(bool *isWriteProtected) {
746// XXX - how is this supposed to work for multi-slot???
747*isWriteProtected = isCardWP( 0 );
748//*isWriteProtected = true;
749#ifdef __DEBUG__
750IOLog("VoodooSDHCI: reportWriteProtection\n");
751#endif
752return kIOReturnSuccess;
753}
754
755/*
756 * setWriteCacheState: Apple API function. This driver does not support
757 *write cacheing. Returns an I/O success.
758 *bool enabled: Enable/disable cache
759 */
760IOReturn VoodooSDHC::setWriteCacheState(bool enabled) {
761#ifdef __DEBUG__
762IOLog("VoodooSDHCI: setWriteCacheState\n");
763#endif
764return kIOReturnSuccess;
765}
766
767/*
768 * reportPollRequirements: Apple API function. Report back requirements for
769 * polling. This driver must be polled, but the poll
770 * is cheap.
771 *bool *pollRequired: This is a return value from this function
772 *bool *pollIsExpensive: This is a return value from this function
773 */
774IOReturn VoodooSDHC::reportPollRequirements(bool *pollRequired, bool *pollIsExpensive)
775{
776*pollRequired = false;
777*pollIsExpensive = false;
778return kIOReturnSuccess;
779}
780
781IOReturn VoodooSDHC::reportMediaState(bool *mediaPresent, bool *changedState)
782{
783IOLockLock(mediaStateLock);
784
785bool presence = isCardPresent(0);
786if (cardPresence == kCardRemount) {
787*changedState = true;
788cardPresence = kCardNotPresent;
789} else if ((cardPresence == kCardIsPresent) == presence) {
790*changedState = false;
791} else {
792*changedState = true;
793if (presence) {
794Reset(0, FULL_RESET);
795cardInit(0);
796::OSSynchronizeIO();
797cardPresence = kCardIsPresent;
798} else {
799cardPresence = kCardNotPresent;
800}
801}
802*mediaPresent = cardPresence == kCardIsPresent;
803
804IOLockUnlock(mediaStateLock);
805return kIOReturnSuccess;
806}
807
808#ifndef __LP64__
809IOReturn VoodooSDHC::reportMaxWriteTransfer(UInt64 blockSize,
810UInt64 *max) {
811//Max blocks we can read at once (see Block Count Register)
812*max = 64 * blockSize;
813
814#ifdef __DEBUG__
815IOLog("VoodooSDHCI: reportMaxWriteTransfer\n");
816#endif /* end DEBUG */
817
818return kIOReturnSuccess;
819}
820
821IOReturn VoodooSDHC::reportMaxReadTransfer (UInt64 blockSize,
822UInt64 *max) {
823//Max blocks we can read at once (see Block Count Register)
824*max = 65536 * blockSize;
825
826#ifdef __DEBUG__
827IOLog("VoodooSDHCI: reportMaxReadTransfer\n");
828#endif /* end DEBUG */
829
830return kIOReturnSuccess;
831}
832#endif /* !__LP64__ */
833
834IOReturn VoodooSDHC::reportMaxValidBlock(UInt64 *maxBlock) {
835*maxBlock = this->maxBlock;
836
837#ifdef __DEBUG__
838IOLog("VoodooSDHCI: reportMaxValidBlock\n");
839#endif
840
841return kIOReturnSuccess;
842}
843
844IOReturn VoodooSDHC::reportLockability(bool *isLockable) {
845*isLockable = false;
846#ifdef __DEBUG__
847IOLog("VoodooSDHCI: reportLockability\n");
848#endif
849return kIOReturnSuccess;
850}
851
852IOReturn VoodooSDHC::reportEjectability(bool *isEjectable) {
853*isEjectable = false;
854#ifdef __DEBUG__
855IOLog("VoodooSDHCI: reportEjectability\n");
856#endif
857return kIOReturnSuccess;
858}
859
860
861IOReturn VoodooSDHC::reportBlockSize(UInt64 *blockSize) {
862// Read/write block size is always 512 bytes. Card's block size
863// is reported but only used in computations of size, not access.
864*blockSize = 512;
865#ifdef __DEBUG__
866IOLog("VoodooSDHCI: reportBlockSize: %d\n", *blockSize);
867#endif
868return kIOReturnSuccess;
869}
870
871IOReturn VoodooSDHC::getWriteCacheState(bool *enabled) {
872*enabled = false;
873#ifdef __DEBUG__
874IOLog("VoodooSDHCI: getWriteCacheState\n");
875#endif
876return kIOReturnSuccess;
877}
878
879char * VoodooSDHC::getVendorString(void) {
880#ifdef __DEBUG__
881IOLog("VoodooSDHCI: getVendorString\n");
882#endif
883return("Generic");
884}
885
886char * VoodooSDHC::getRevisionString(void) {
887#ifdef __DEBUG__
888IOLog("VoodooSDHCI: getRevisionString\n");
889#endif
890return("2");
891}
892
893char * VoodooSDHC::getProductString(void) {
894#ifdef __DEBUG__
895IOLog("VoodooSDHCI: getProductString\n");
896#endif
897return("SDHCI Controller");
898}
899
900char * VoodooSDHC::getAdditionalDeviceInfoString(void) {
901#ifdef __DEBUG__
902IOLog("VoodooSDHCI: getAdditionalDeviceInfoString\n");
903#endif
904return("VoodooSDHCI:getAdditionalDeviceInfoString");
905}
906
907IOReturn VoodooSDHC::doSynchronizeCache(void) {
908#ifdef __DEBUG__
909IOLog("VoodooSDHCI: doSynchronizeCache\n");
910#endif
911return kIOReturnSuccess;
912}
913
914IOReturn VoodooSDHC::doLockUnlockMedia(bool doLock) {
915#ifdef __DEBUG__
916IOLog("VoodooSDHCI: doLockUnlockMedia\n");
917#endif
918return kIOReturnUnsupported;
919}
920
921UInt32 VoodooSDHC::doGetFormatCapacities(UInt64 *capacities, UInt32 capacitiesMaxCount) const {
922#ifdef __DEBUG__
923IOLog("VoodooSDHCI: doGetFormatCapacities\n");
924#endif
925return kIOReturnSuccess;
926}
927
928IOReturn VoodooSDHC::doFormatMedia(UInt64 byteCapacity) {
929#ifdef __DEBUG__
930IOLog("VoodooSDHCI: doFormatMedia\n");
931#endif
932return kIOReturnUnsupported;
933}
934
935IOReturn VoodooSDHC::doEjectMedia(void) {
936#ifdef __DEBUG__
937IOLog("VoodooSDHCI: doEjectMedia\n");
938#endif
939return kIOReturnUnsupported;
940}
941
942bool VoodooSDHC::waitIntStatus(UInt32 maskBits)
943{
944// roughly 5 seconds before timeout
945for (int cnt = 0; cnt < 500000; cnt++) {
946while (1) {
947UInt32 nis = PCIRegP[0]->NormalIntStatus;
948if (nis & ErrorInterrupt) {
949return false;
950}
951if (nis & maskBits) {
952PCIRegP[0]->NormalIntStatus = nis | maskBits;
953return true;
954}
955}
956::IODelay(10);
957}
958return false;
959}
960
961/*
962 * readBlockMulti_pio: Read a set of blocks using multi-block reads and PIO
963 * not DMA mode. The host controller must be locked when
964 *this function is called.
965 *IOMemoryDescriptor *buffer: Buffer operation class. Defines
966 *read/write, address of operation, etc.
967 *UInt32 block: Block offset to read/write
968 *UInt32 nblks: Block count to read/write
969 *UInt32 offset: Offset from beginning of transfer - where in
970 *final buffer we should begin placing data
971 */
972IOReturn VoodooSDHC::readBlockMulti_pio(IOMemoryDescriptor *buffer,
973UInt32 block, UInt32 nblks,
974UInt32 offset) {
975UInt8 buff[512];// Temporary storage for one block
976UInt32 *pBuff;
977IOReturn ret = kIOReturnError;
978
979#ifdef __DEBUG__
980IOLog("VoodooSDHCI: readBlockMulti_pio: block = %d, nblks = %d\n", block, nblks);
981#endif /* __DEBUG__ */
982
983pBuff = (UInt32*)buff;
984
985#ifndef NO_RESET_WAR
986// Reset card before every operation. The Linux driver
987// does this for this host controller. Not sure why.
988Reset(0, CMD_RESET);
989Reset(0, DAT_RESET);
990#endif
991
992/* Enable all interrupts */
993this->PCIRegP[0]->NormalIntStatusEn = -1;
994this->PCIRegP[0]->ErrorIntStatusEn = -1;
995
996/* Clear pending interrupts */
997this->PCIRegP[0]->NormalIntStatus =
998(BuffReadReady | XferComplete | CmdComplete);
999
1000/* Set maximum timeout value */
1001this->PCIRegP[0]->TimeoutControl = 0xe;
1002
1003*(volatile UInt32 *)&(this->PCIRegP[0]->NormalIntStatus) =
1004*(volatile UInt32 *)&(this->PCIRegP[0]->NormalIntStatus);
1005
1006this->PCIRegP[0]->BlockSize = 512;
1007this->PCIRegP[0]->BlockCount = nblks;
1008
1009this->PCIRegP[0]->TransferMode = SDHCI_TRNS_READ | SDHCI_TRNS_MULTI |
1010 SDHCI_TRNS_BLK_CNT_EN | SDHCI_TRNS_ACMD12;
1011
1012
1013// Issue read command to host controller
1014SDCommand(0, SD_READ_MULTIPLE_BLOCK, SDCR18, isHighCapacity ? block : block * 512);
1015
1016// wait for CmdComplete
1017if (! waitIntStatus(CmdComplete)) {
1018IOLog("VoodooSDHCI: I/O error after command 18: It Status: 0x%x\n", PCIRegP[0]->NormalIntStatus);
1019goto out;
1020}
1021
1022for (int i = 0; i < nblks; i++) {
1023// wait for BufferReadReady
1024if (! waitIntStatus(BuffReadReady)) {
1025IOLog("VoodooSDHCI: I/O timeout while waiting for data, Status: 0x%0x\n", PCIRegP[0]->NormalIntStatus);
1026goto out;
1027
1028}
1029/* Read block from card */
1030read_block_pio(&this->PCIRegP[0]->BufferDataPort, pBuff);
1031
1032/* Copy buffer to final location */
1033buffer->writeBytes((i + offset) * 512, buff, 1 * 512);
1034}
1035
1036// wait for transfer complete
1037if (! waitIntStatus(XferComplete)) {
1038IOLog("VoodooSDHCI: I/O timeout during completion... status == 0x%x\n", PCIRegP[0]->NormalIntStatus);
1039}
1040ret = kIOReturnSuccess;
1041out:
1042return ret;
1043}
1044
1045/*
1046 * sdma_access: Read / write a set of blocks using multi-block reads in SDMA mode.
1047 * The host controller must be locked when this function is called.
1048 *IOMemoryDescriptor *buffer: Buffer operation class. Defines
1049 *read/write, address of operation, etc.
1050 *UInt32 block: Block offset to read/write
1051 *UInt32 nblks: Block count to read/write
1052 * bool read: true if read, false if write
1053 */
1054IOReturn VoodooSDHC::sdma_access(IOMemoryDescriptor *buffer,
1055UInt32 block, UInt32 nblks, bool read) {
1056IOReturn ret = kIOReturnError;
1057UInt32 nis, offset = 0;
1058AbsoluteTime deadline;
1059
1060#ifdef __DEBUG__
1061IOLog("VoodooSDHCI readBlockMulti_sdma: block = %d, nblks = %d\n", block, nblks);
1062#endif /* __DEBUG__ */
1063#ifndef NO_RESET_WAR
1064// Reset card before every operation. The Linux driver
1065// does this for this host controller. Not sure why.
1066Reset(0, CMD_RESET);
1067Reset(0, DAT_RESET);
1068#endif
1069
1070/* write: fill in data */
1071if (! read) {
1072buffer->readBytes(offset * 512, virtSdmaBuff, min(SDMA_BUFFER_SIZE, nblks * 512));
1073offset += min(SDMA_BUFFER_SIZE / 512, nblks);
1074}
1075
1076/* Set maximum timeout value */
1077this->PCIRegP[0]->TimeoutControl = 0xe; // 2^27clks / 50MHz = 2.7 seconds
1078
1079/* Enable all interrupts */
1080this->PCIRegP[0]->NormalIntSignalEn = 0x01ff;
1081this->PCIRegP[0]->NormalIntStatusEn = -1;
1082this->PCIRegP[0]->ErrorIntSignalEn = 0x01ff;
1083this->PCIRegP[0]->ErrorIntStatusEn = -1;
1084
1085/* Clear pending interrupts */
1086this->PCIRegP[0]->NormalIntStatus =
1087(BuffReadReady | XferComplete | CmdComplete);
1088this->PCIRegP[0]->ErrorIntStatus = 0xf3ff;
1089
1090::OSSynchronizeIO();
1091PCIRegP[0]->SDMASysAddr = physSdmaBuff;
1092::OSSynchronizeIO();
1093this->PCIRegP[0]->BlockSize = 512 | SDMA_BUFFER_SIZE_IN_REG;
1094this->PCIRegP[0]->BlockCount = nblks;
1095::OSSynchronizeIO();
1096
1097// Issue read command to host controller
1098SDCommand(0,
1099read ? SD_READ_MULTIPLE_BLOCK : SD_WRITE_MULTIPLE_BLOCK,
1100SDCR18,
1101isHighCapacity ? block : block * 512);
1102::OSSynchronizeIO();
1103
1104// wait for CmdComplete
1105if (! waitIntStatus(CmdComplete)) {
1106IOLog("VoodooSDHCI: I/O error after command %d (SDMA): Status: 0x%x, Error: 0x%x\n",
1107read ? SD_READ_MULTIPLE_BLOCK : SD_WRITE_MULTIPLE_BLOCK, PCIRegP[0]->NormalIntStatus, PCIRegP[0]->ErrorIntStatus);
1108Reset(0, FULL_RESET);
1109if (! cardInit(0)) {
1110IOLog("VoodooSDHCI: reset failed, disabling access\n");
1111cardPresence = kCardRemount;
1112}
1113ret = kIOReturnTimeout;
1114goto out;
1115}
1116// check response
1117if (PCIRegP[0]->Response[0] & (read ? 0xcff80000 : 0xeff80000)) {
1118IOLog("VoodooSDHCI: Unexpected response from command %d (SDMA): Response: 0x%x\n",
1119read ? SD_READ_MULTIPLE_BLOCK : SD_WRITE_MULTIPLE_BLOCK, PCIRegP[0]->Response[0]);
1120goto out;
1121}
1122
1123clock_interval_to_deadline(5000, kMillisecondScale, (uint64_t*)&deadline);
1124IOLockLock(sdmaCond);
1125while ((PCIRegP[0]->NormalIntStatus & ErrorInterrupt) == 0) {
1126if (IOLockSleepDeadline(sdmaCond, sdmaCond, deadline, THREAD_UNINT) == THREAD_TIMED_OUT) {
1127IOLockUnlock(sdmaCond);
1128// timeout
1129IOLog("VoodooSDHCI: I/O timeout during SDMA transfer: Status: 0x%x, Error: 0x%x, Block: %d, Offset: %d, Blocks: %d\n",
1130PCIRegP[0]->NormalIntStatus, PCIRegP[0]->ErrorIntStatus, (int)block, (int)offset, (int)nblks);
1131ret = kIOReturnTimeout;
1132goto out;
1133}
1134nis = PCIRegP[0]->NormalIntStatus;
1135if (nis & XferComplete) {
1136IOLockUnlock(sdmaCond);
1137if (read) {
1138buffer->writeBytes(offset * 512, virtSdmaBuff, nblks * 512);
1139}
1140PCIRegP[0]->NormalIntStatus = XferComplete | DMAInterrupt;
1141ret = kIOReturnSuccess;
1142goto out;
1143} else if (nis & DMAInterrupt) {
1144IOLockUnlock(sdmaCond);
1145if (read) {
1146buffer->writeBytes(offset * 512, virtSdmaBuff, SDMA_BUFFER_SIZE);
1147offset += SDMA_BUFFER_SIZE / 512;
1148nblks -= SDMA_BUFFER_SIZE / 512;
1149} else {
1150buffer->readBytes(offset * 512, virtSdmaBuff, min(SDMA_BUFFER_SIZE, (nblks - offset) * 512));
1151offset += min(SDMA_BUFFER_SIZE / 512, nblks - offset);
1152}
1153IOLockLock(sdmaCond);
1154PCIRegP[0]->NormalIntStatus = DMAInterrupt;
1155::OSSynchronizeIO();
1156PCIRegP[0]->SDMASysAddr = physSdmaBuff;
1157}
1158}
1159IOLockUnlock(sdmaCond);
1160// error
1161IOLog("VoodooSDHCI: I/O error during SDMA transfer: Status: 0x%x, Error: 0x%x, Block: %d, Offset: %d, Blocks: %d\n",
1162PCIRegP[0]->NormalIntStatus, PCIRegP[0]->ErrorIntStatus, (int)block, (int)offset, (int)nblks);
1163goto out;
1164
1165ret = kIOReturnSuccess;
1166out:
1167PCIRegP[0]->NormalIntSignalEn = 0;
1168PCIRegP[0]->ErrorIntSignalEn = 0;
1169return ret;
1170}
1171
1172
1173
1174/*
1175 * readBlockSingle_pio: Read a single block from the card using PIO not DMA
1176 * mode. The host controller must be locked when this
1177 * function is called.
1178 *UInt8 *buff: Temporary buffer for data
1179 *UInt32 block: Block offset to read/write
1180 */
1181IOReturn VoodooSDHC::readBlockSingle_pio(UInt8 *buff, UInt32 block) {
1182UInt32 *pBuff;
1183int cnt, pass;
1184IOReturn ret;
1185
1186#ifndef NO_RESET_WAR
1187// Reset card before every operation. The Linux driver does this for
1188// this host controller. Not sure why
1189Reset(0, CMD_RESET);
1190Reset(0, DAT_RESET);
1191#endif /* NO_RESET_WAR */
1192
1193pBuff = (UInt32*)buff;
1194
1195/* Set transfer mode to single block */
1196this->PCIRegP[0]->TransferMode = BIT4;
1197
1198/* Enable interrupt flags */
1199this->PCIRegP[0]->NormalIntStatusEn = -1;
1200this->PCIRegP[0]->ErrorIntStatusEn = -1;
1201
1202/* Clear pending interrupts */
1203this->PCIRegP[0]->NormalIntStatus =
1204(BuffReadReady | XferComplete | CmdComplete);
1205
1206/* Set maximum timeout value */
1207this->PCIRegP[0]->TimeoutControl = 0xe;
1208
1209#ifdef __DEBUG__
1210IOLog("VoodooSDHCI Int Status 0x%x Timeout = 0x%x\n",
1211*(volatile UInt32 *)&(this->PCIRegP[0]->NormalIntStatus),
1212this->PCIRegP[0]->TimeoutControl);
1213
1214//IOLog("VoodooSDHCI: state1 = 0x%x response = 0x%x\n",
1215//this->PCIRegP[0]->PresentState, this->PCIRegP[0]->Response[0]);
1216#endif /* __DEBUG__ */
1217
1218*(volatile UInt32 *)&(this->PCIRegP[0]->NormalIntStatus) =
1219*(volatile UInt32 *)&(this->PCIRegP[0]->NormalIntStatus);
1220
1221SDCommand(0, SD_READ_SINGLE_BLOCK, SDCR17, isHighCapacity ? block : block * 512);
1222
1223cnt = pass = 0;
1224
1225//IOLog("VoodooSDHCI: state2 = 0x%x response = 0x%x\n",
1226//this->PCIRegP[0]->PresentState, this->PCIRegP[0]->Response[0]);
1227
1228while((this->PCIRegP[0]->NormalIntStatus & BuffReadReady) !=
1229BuffReadReady) {
1230if (this->PCIRegP[0]->NormalIntStatus & 0x8000) {
1231IOLog("VoodooSDHCI: S Returning error: 0x%x\n", *(volatile UInt32 *) & (this->PCIRegP[0]->NormalIntStatus));
1232ret = kIOReturnError;
1233goto out;
1234}
1235cnt++;
1236if (cnt > 100000) {
1237cnt = 0;
1238pass++;
1239IOLog("VoodooSDHCI: Stuck in while loop 1: pass = %d"
1240" status = 0x%x state = 0x%x\n", pass,
1241*(volatile UInt32 *)&(this->PCIRegP[0]->
1242NormalIntStatus),
1243this->PCIRegP[0]->PresentState);
1244if (pass > 10) {
1245ret = kIOReturnError;
1246goto out;
1247}
1248}
1249::IODelay(10);
1250}
1251
1252/* Read block from card */
1253read_block_pio(&this->PCIRegP[0]->BufferDataPort, pBuff);
1254
1255ret = kIOReturnSuccess;
1256
1257out:
1258this->PCIRegP[0]->NormalIntStatus =
1259(BuffReadReady|XferComplete|CmdComplete);
1260return ret;
1261}
1262
1263/*
1264 * writeBlockMulti_pio: Write multiple blocks to the card using PIO not DMA
1265 * mode. The host controller must be locked when this
1266 * function is called.
1267 *IOMemoryDescriptor *buffer: Buffer operation class. Defines
1268 *read/write, address of operation, etc.
1269 *UInt32 block: Block offset to read/write
1270 *UInt32 nblks: Number of blocks to read/write
1271 *UInt32 offset: Offset from beginning of transfer - where in
1272 *final buffer we should begin placing data
1273 */
1274IOReturn VoodooSDHC::writeBlockMulti_pio(IOMemoryDescriptor *buffer,
1275UInt32 block, UInt32 nblks, UInt32 offset) {
1276UInt8 buff[512];// Temporary storage for data block
1277int cnt, pass;
1278UInt32 *pBuff;
1279IOReturn ret;
1280
1281#ifndef NO_RESET_WAR
1282// Reset card before every operation. The Linux driver does this
1283// for this host controller. Not sure why.
1284Reset(0, CMD_RESET);
1285Reset(0, DAT_RESET);
1286#endif
1287
1288this->PCIRegP[0]->NormalIntStatusEn = -1;
1289this->PCIRegP[0]->ErrorIntStatusEn = -1;
1290this->PCIRegP[0]->NormalIntStatus =
1291BuffWriteReady | XferComplete | CmdComplete;
1292this->PCIRegP[0]->TimeoutControl = 0xe;
1293
1294this->PCIRegP[0]->BlockSize = 512;
1295this->PCIRegP[0]->BlockCount = nblks;
1296
1297SDCommand(0, SD_APP_CMD, SDCR55, this->RCA << 16);
1298SDCommand(0, SD_APP_SET_WR_BLK_ERASE_COUNT, SDCR23, nblks);
1299SDCommand(0, SD_WRITE_MULTIPLE_BLOCK, SDCR24, isHighCapacity ? block : block * 512);
1300
1301for (int i = 0; i < nblks; i++) {
1302buffer->readBytes((offset + i) * 512, buff, 1 * 512);
1303
1304pBuff = (UInt32*)buff;
1305
1306cnt = pass = 0;
1307 while(!(this->PCIRegP[0]->PresentState &
1308 SDHCI_SPACE_AVAILABLE)) {
1309cnt++;
1310
1311if (this->PCIRegP[0]->NormalIntStatus & 0x8000) {
1312IOLog("VoodooSDHCI 2 Returning error: 0x%x\n",
1313 *(volatile UInt32 *)
1314 &(this->PCIRegP[0]->NormalIntStatus));
1315ret = kIOReturnError;
1316goto out;
1317}
1318if (cnt > 100000) {
1319cnt = 0;
1320pass++;
1321IOLog("VoodooSDHCI: Stuck in while loop 2: pass = %d"
1322 " status = 0x%x\n", pass,
1323 this->PCIRegP[0]->NormalIntStatus);
1324if (pass > 10) {
1325ret = kIOReturnError;
1326goto out;
1327}
1328}
1329::IODelay(10);
1330}
1331
1332 this->PCIRegP[0]->NormalIntStatus =
1333 this->PCIRegP[0]->NormalIntStatus;
1334
1335for (int j = 0; j < 128; j++) {
1336this->PCIRegP[0]->BufferDataPort = *pBuff;
1337pBuff++;
1338}
1339
1340}
1341
1342cnt = pass = 0;
1343while ((this->PCIRegP[0]->NormalIntStatus & XferComplete) !=
1344 XferComplete) {
1345cnt++;
1346if (this->PCIRegP[0]->NormalIntStatus & 0x8000) {
1347IOLog("VoodooSDHCI 3 Returning error: 0x%x\n",
1348 *(volatile UInt32 *)
1349 &(this->PCIRegP[0]->NormalIntStatus));
1350ret = kIOReturnError;
1351goto out;
1352}
1353
1354if (cnt > 100000) {
1355cnt = 0;
1356pass++;
1357IOLog("VoodooSDHCI: Stuck in while loop 3: pass = %d"
1358 " status = 0x%x\n", pass,
1359 this->PCIRegP[0]->NormalIntStatus);
1360if (pass > 10) {
1361ret = kIOReturnError;
1362goto out;
1363}
1364}
1365}
1366this->PCIRegP[0]->NormalIntStatus =
1367BuffWriteReady | XferComplete | CmdComplete;
1368ret = kIOReturnSuccess;
1369
1370out:
1371return ret;
1372}
1373
1374/*
1375 * writeBlockSingle_pio: Read a single block from the card using PIO not DMA
1376 * mode. The host controller must be locked when this
1377 * function is called.
1378 *IOMemoryDescriptor *buffer: Buffer operation class. Defines
1379 *read/write, address of operation, etc.
1380 *UInt32 block: Block offset to read/write
1381 *UInt32 offset: Offset from beginning of transfer - where in
1382 *final buffer we should begin placing data
1383 */
1384IOReturn VoodooSDHC::writeBlockSingle_pio(IOMemoryDescriptor *buffer,
1385UInt32 block, UInt32 offset) {
1386UInt8 buff[512];// Temporary storage for data block
1387int cnt, pass;
1388UInt32 *pBuff;
1389IOReturn ret;
1390
1391pBuff = (UInt32*)buff;
1392
1393buffer->readBytes(offset * 512, buff, 1 * 512);
1394
1395#ifndef NO_RESET_WAR
1396// Reset card before every operation. The Linux driver does this
1397// for this host controller. Not sure why.
1398Reset(0, CMD_RESET);
1399Reset(0, DAT_RESET);
1400#endif
1401
1402this->PCIRegP[0]->TransferMode = 0;
1403this->PCIRegP[0]->NormalIntStatusEn = -1;
1404this->PCIRegP[0]->ErrorIntStatusEn = -1;
1405this->PCIRegP[0]->NormalIntStatus =
1406BuffWriteReady | XferComplete | CmdComplete;
1407this->PCIRegP[0]->TimeoutControl = 0xe;
1408
1409SDCommand(0, SD_WRITE_BLOCK, SDCR24, isHighCapacity ? block : block * 512);
1410
1411cnt = pass = 0;
1412while ((this->PCIRegP[0]->NormalIntStatus & BuffWriteReady) !=
1413BuffWriteReady) {
1414cnt++;
1415
1416if (this->PCIRegP[0]->NormalIntStatus & 0x8000) {
1417IOLog("VoodooSDHCI: 2 Returning error: 0x%x\n",
1418*(volatile UInt32 *)
1419&(this->PCIRegP[0]->NormalIntStatus));
1420ret = kIOReturnError;
1421goto out;
1422}
1423
1424if (cnt > 100000) {
1425cnt = 0;
1426pass++;
1427IOLog("VoodooSDHCI: Stuck in while loop 2: pass = %d"
1428" status = 0x%x\n", pass,
1429this->PCIRegP[0]->NormalIntStatus);
1430if (pass > 10) {
1431ret = kIOReturnError;
1432goto out;
1433}
1434}
1435}
1436
1437for (int j = 0; j < 128; j++) {
1438this->PCIRegP[0]->BufferDataPort = *pBuff;
1439pBuff++;
1440}
1441
1442cnt = pass = 0;
1443while ((this->PCIRegP[0]->NormalIntStatus & XferComplete) !=
1444XferComplete) {
1445cnt++;
1446
1447if (this->PCIRegP[0]->NormalIntStatus & 0x8000) {
1448IOLog("VoodooSDHCI 3 Returning error: 0x%x\n",
1449*(volatile UInt32 *)
1450&(this->PCIRegP[0]->NormalIntStatus));
1451ret = kIOReturnError;
1452goto out;
1453}
1454
1455if (cnt > 100000) {
1456cnt = 0;
1457pass++;
1458IOLog("VoodooSDHCI: stuck in while loop #3: pass = %d"
1459" status = 0x%x\n", pass,
1460this->PCIRegP[0]->NormalIntStatus);
1461if (pass > 10) {
1462ret = kIOReturnError;
1463goto out;
1464}
1465}
1466}
1467this->PCIRegP[0]->NormalIntStatus =
1468BuffWriteReady | XferComplete | CmdComplete;
1469ret = kIOReturnSuccess;
1470
1471out:
1472return ret;
1473}
1474
1475/*
1476 * doAsyncReadWrite: Guts of the driver. Perform reads and writes. This
1477 * function must be reentrant. Further, the completion
1478 * action may result in another call to this function.
1479 * This function may block.
1480 * Returns success or failure.
1481 *IOMemoryDescriptor *buffer: Buffer operation class. Defines
1482 *read/write, address of operation, etc.
1483 *UInt32 block: Block offset to read/write
1484 *
1485 *IOStorageCompletion completion: Action to perform upon
1486 *completion of operation
1487 */
1488#ifdef __LP64__
1489IOReturn VoodooSDHC::doAsyncReadWrite(IOMemoryDescriptor *buffer,
1490 UInt64 block, UInt64 nblks,
1491 IOStorageAttributes *attributes,
1492 IOStorageCompletion *completion) {
1493UInt8 buff[512];// Temporary storage for data block
1494int ret = 0;
1495int locked;
1496UInt64 blk, n;
1497
1498// All access to the card must be done while this lock is held
1499lock.lock();
1500locked = 1;
1501
1502if (cardPresence != kCardIsPresent || ! isCardPresent(0)) {
1503ret = kIOReturnNoMedia;
1504goto out;
1505}
1506#ifdef __DEBUG__
1507IOLog("VoodooSDHCI: in doAsyncReadWrite function :: block == %d, nblks == %d, ", block, nblks);
1508#endif
1509if (buffer->getDirection() == kIODirectionIn) {
1510/* Read from Card */
1511#ifdef __DEBUG__
1512IOLog("READING FROM CARD!\n");
1513#endif
1514blk = block;
1515n = nblks;
1516while (n) {
1517if (USE_SDMA) {
1518int i;
1519for (i = 0; i < SDMA_RETRY_COUNT; i++)
1520if ((ret = sdma_access(buffer, block, nblks, true)) != kIOReturnTimeout)
1521break;
1522if (i != 0)
1523IOLog("VoodooSDHCI: retry succeeded\n");
1524n = 0;
1525} else if ((nblks > 1) && USE_MULTIBLOCK) {
1526int b = MIN(2048 /* should fit in sdma buff */, n);
1527
1528ret = readBlockMulti_pio(buffer, blk, b,
1529 blk - block);
1530n -= b;
1531blk += b;
1532} else {
1533ret = readBlockSingle_pio(buff, blk);
1534buffer->writeBytes((blk - block) * 512,
1535 buff, 1 * 512);
1536n--;
1537blk++;
1538}
1539if (ret != kIOReturnSuccess) goto out;
1540#ifdef __DEBUG__
1541IOLog("VoodooSDHCI: ret = 0x%x block = %d\n", ret, blk);;
1542#endif /* __DEBUG__ */
1543}
1544} else {
1545/* Write to Card */
1546#ifdef __DEBUG__
1547IOLog("WRITING TO CARD!\n");
1548#endif
1549
1550#ifdef READONLY_DRIVER
1551// When compiled in this mode, the driver fails all write
1552// operations. Useful for testing to gain confidence in
1553// code without trashing data. Define must be set at top
1554// of file.
1555ret = kIOReturnError;
1556goto out;
1557#endif
1558blk = block;
1559n = nblks;
1560while (n) {
1561if (USE_SDMA) {
1562int i;
1563for (i = 0; i < SDMA_RETRY_COUNT; i++)
1564if ((ret = sdma_access(buffer, block, nblks, false)) != kIOReturnTimeout)
1565break;
1566if (i != 0)
1567IOLog("VoodooSDHCI: retry succeeded\n");
1568n = 0;
1569}
1570else if ((nblks > 1) && USE_MULTIBLOCK) {
1571int b = MIN(2048, n);
1572ret = writeBlockMulti_pio(buffer, blk, b,
1573 blk - block);
1574n -= b;
1575blk += b;
1576} else {
1577ret = writeBlockSingle_pio(buffer, blk,
1578 blk - block);
1579n--;
1580blk++;
1581}
1582if (ret != kIOReturnSuccess) goto out;
1583}
1584
1585}
1586
1587locked = 0;
1588lock.unlock();
1589
1590if(completion->action) {
1591(completion->action)(completion->target, completion->parameter, kIOReturnSuccess, nblks * 512);
1592} else {
1593IOLog("VoodooSDHCI ERROR!\n");
1594ret = kIOReturnError;
1595goto out;
1596}
1597ret = kIOReturnSuccess;
1598
1599out:
1600switch (ret) {
1601case kIOReturnSuccess:
1602break;
1603case kIOReturnNoMedia:
1604/* require remount */
1605cardPresence = kCardRemount;
1606IOLog("VoodooSDHCI: media not present, require remount\n");
1607break;
1608}
1609if (locked)
1610lock.unlock();
1611//in_read_write = 0;
1612return ret;
1613}
1614#else /* !__LP64__ */
1615IOReturn VoodooSDHC::doAsyncReadWrite(IOMemoryDescriptor *buffer,
1616UInt32 block, UInt32 nblks, IOStorageCompletion completion) {
1617UInt8 buff[512];// Temporary storage for data block
1618int ret = 0;
1619int locked;
1620UInt32 blk, n;
1621
1622// All access to the card must be done while this lock is held
1623lock.lock();
1624locked = 1;
1625
1626if (cardPresence != kCardIsPresent || ! isCardPresent(0)) {
1627ret = kIOReturnNoMedia;
1628goto out;
1629}
1630#ifdef __DEBUG__
1631IOLog("VoodooSDHCI: in doAsyncReadWrite function :: block == %d, nblks == %d, ", block, nblks);
1632#endif
1633if (buffer->getDirection() == kIODirectionIn) {
1634/* Read from Card */
1635#ifdef __DEBUG__
1636IOLog("READING FROM CARD!\n");
1637#endif
1638blk = block;
1639n = nblks;
1640while (n) {
1641if (USE_SDMA) {
1642int i;
1643for (i = 0; i < SDMA_RETRY_COUNT; i++)
1644if ((ret = sdma_access(buffer, block, nblks, true)) != kIOReturnTimeout)
1645break;
1646if (i != 0)
1647IOLog("VoodooSDHCI: retry succeeded\n");
1648n = 0;
1649} else if ((nblks > 1) && USE_MULTIBLOCK) {
1650int b = MIN(2048 /* should fit in sdma buff */, n);
1651
1652ret = readBlockMulti_pio(buffer, blk, b,
1653blk - block);
1654n -= b;
1655blk += b;
1656} else {
1657ret = readBlockSingle_pio(buff, blk);
1658buffer->writeBytes((blk - block) * 512,
1659buff, 1 * 512);
1660n--;
1661blk++;
1662}
1663if (ret != kIOReturnSuccess) goto out;
1664#ifdef __DEBUG__
1665IOLog("VoodooSDHCI: ret = 0x%x block = %d\n", ret, blk);;
1666#endif /* __DEBUG__ */
1667}
1668} else {
1669/* Write to Card */
1670#ifdef __DEBUG__
1671IOLog("WRITING TO CARD!\n");
1672#endif
1673
1674#ifdef READONLY_DRIVER
1675// When compiled in this mode, the driver fails all write
1676// operations. Useful for testing to gain confidence in
1677// code without trashing data. Define must be set at top
1678// of file.
1679ret = kIOReturnError;
1680goto out;
1681#endif
1682blk = block;
1683n = nblks;
1684while (n) {
1685if (USE_SDMA) {
1686int i;
1687for (i = 0; i < SDMA_RETRY_COUNT; i++)
1688if ((ret = sdma_access(buffer, block, nblks, false)) != kIOReturnTimeout)
1689break;
1690if (i != 0)
1691IOLog("VoodooSDHCI: retry succeeded\n");
1692n = 0;
1693}
1694else if ((nblks > 1) && USE_MULTIBLOCK) {
1695int b = MIN(2048, n);
1696ret = writeBlockMulti_pio(buffer, blk, b,
1697blk - block);
1698n -= b;
1699blk += b;
1700} else {
1701ret = writeBlockSingle_pio(buffer, blk,
1702blk - block);
1703n--;
1704blk++;
1705}
1706if (ret != kIOReturnSuccess) goto out;
1707}
1708
1709}
1710
1711locked = 0;
1712lock.unlock();
1713
1714if(completion.action) {
1715(*completion.action)(completion.target, completion.parameter, kIOReturnSuccess, nblks * 512);
1716} else {
1717IOLog("VoodooSDHCI ERROR!\n");
1718ret = kIOReturnError;
1719goto out;
1720}
1721ret = kIOReturnSuccess;
1722
1723out:
1724switch (ret) {
1725case kIOReturnSuccess:
1726break;
1727case kIOReturnNoMedia:
1728/* require remount */
1729cardPresence = kCardRemount;
1730IOLog("VoodooSDHCI: media not present, require remount\n");
1731break;
1732}
1733if (locked)
1734lock.unlock();
1735//in_read_write = 0;
1736return ret;
1737}
1738#endif /* !__LP64__ */
1739
1740
1741void VoodooSDHC::handleInterrupt()
1742{
1743IOLockLock(sdmaCond);
1744IOLockWakeup(sdmaCond, sdmaCond, true);
1745IOLockUnlock(sdmaCond);
1746}
1747
1748void VoodooSDHC::handleTimer()
1749{
1750bool mediaPresent, changedState;
1751reportMediaState(&mediaPresent, &changedState);
1752if (changedState)
1753messageClients(
1754kIOMessageMediaStateHasChanged,
1755(void*)(mediaPresent ? kIOMediaStateOnline: kIOMediaStateOffline),
17560);
1757timerSrc->setTimeoutMS(1000);
1758}
1759
1760void VoodooSDHC::interruptHandler(OSObject *owner, IOInterruptEventSource *, int)
1761{
1762VoodooSDHC *self = static_cast<VoodooSDHC*>(owner);
1763self->handleInterrupt();
1764}
1765
1766bool VoodooSDHC::interruptFilter(OSObject *owner, IOFilterInterruptEventSource *)
1767{
1768if (OSDynamicCast(VoodooSDHC, owner)) {
1769return true;
1770}
1771return false;
1772}
1773
1774void VoodooSDHC::timerHandler(OSObject *owner, IOTimerEventSource *)
1775{
1776VoodooSDHC *self = static_cast<VoodooSDHC*>(owner);
1777self->handleTimer();
1778}
1779

Archive Download this file

Revision: HEAD