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