xref: /haiku/src/add-ons/kernel/busses/scsi/ahci/ahci_controller.cpp (revision 204dee708a999d5a71d0cb9497650ee7cef85d0a)
1 /*
2  * Copyright 2007-2009, Marcus Overhagen. All rights reserved.
3  * Distributed under the terms of the MIT License.
4  */
5 
6 #include "ahci_controller.h"
7 #include "util.h"
8 
9 #include <algorithm>
10 #include <KernelExport.h>
11 #include <stdio.h>
12 #include <string.h>
13 #include <new>
14 
15 #define TRACE(a...) dprintf("ahci: " a)
16 #define FLOW(a...)	dprintf("ahci: " a)
17 
18 
19 AHCIController::AHCIController(device_node *node,
20 		pci_device_module_info *pciModule, pci_device *device)
21 	:
22 	fNode(node),
23 	fPCI(pciModule),
24 	fPCIDevice(device),
25 	fPCIVendorID(0xffff),
26 	fPCIDeviceID(0xffff),
27 	fFlags(0),
28 	fCommandSlotCount(0),
29 	fPortCount(0),
30 	fPortImplementedMask(0),
31 	fIRQ(0),
32 	fInstanceCheck(-1)
33 {
34 	memset(fPort, 0, sizeof(fPort));
35 
36 	ASSERT(sizeof(ahci_port) == 128);
37 	ASSERT(sizeof(ahci_hba) == 4352);
38 	ASSERT(sizeof(fis) == 256);
39 	ASSERT(sizeof(command_list_entry) == 32);
40 	ASSERT(sizeof(command_table) == 128);
41 	ASSERT(sizeof(prd) == 16);
42 }
43 
44 
45 AHCIController::~AHCIController()
46 {
47 }
48 
49 
50 status_t
51 AHCIController::Init()
52 {
53 	pci_info pciInfo;
54 	fPCI->get_pci_info(fPCIDevice, &pciInfo);
55 
56 	fPCIVendorID = pciInfo.vendor_id;
57 	fPCIDeviceID = pciInfo.device_id;
58 
59 	TRACE("AHCIController::Init %u:%u:%u vendor %04x, device %04x\n",
60 		pciInfo.bus, pciInfo.device, pciInfo.function, fPCIVendorID, fPCIDeviceID);
61 
62 // --- Instance check workaround begin
63 	char sName[32];
64 	snprintf(sName, sizeof(sName), "ahci-inst-%u-%u-%u", pciInfo.bus, pciInfo.device, pciInfo.function);
65 	if (find_port(sName) >= 0) {
66 		dprintf("AHCIController::Init ERROR: an instance for object %u:%u:%u already exists\n",
67 			pciInfo.bus, pciInfo.device, pciInfo.function);
68 		return B_ERROR;
69 	}
70 	fInstanceCheck = create_port(1, sName);
71 // --- Instance check workaround end
72 
73 	get_device_info(fPCIVendorID, fPCIDeviceID, NULL, &fFlags);
74 
75 	uchar capabilityOffset;
76 	status_t res = fPCI->find_pci_capability(fPCIDevice, PCI_cap_id_sata, &capabilityOffset);
77 	if (res == B_OK) {
78 		uint32 satacr0;
79 		uint32 satacr1;
80 		TRACE("PCI SATA capability found at offset 0x%x\n", capabilityOffset);
81 		satacr0 = fPCI->read_pci_config(fPCIDevice, capabilityOffset, 4);
82 		satacr1 = fPCI->read_pci_config(fPCIDevice, capabilityOffset + 4, 4);
83 		TRACE("satacr0 = 0x%08" B_PRIx32 ", satacr1 = 0x%08" B_PRIx32 "\n",
84 			satacr0, satacr1);
85 	}
86 
87 	uint16 pcicmd = fPCI->read_pci_config(fPCIDevice, PCI_command, 2);
88 	TRACE("pcicmd old 0x%04x\n", pcicmd);
89 	pcicmd &= ~(PCI_command_io | PCI_command_int_disable);
90 	pcicmd |= PCI_command_master | PCI_command_memory;
91 	TRACE("pcicmd new 0x%04x\n", pcicmd);
92 	fPCI->write_pci_config(fPCIDevice, PCI_command, 2, pcicmd);
93 
94 	if (fPCIVendorID == PCI_VENDOR_JMICRON) {
95 		uint32 ctrl = fPCI->read_pci_config(fPCIDevice, PCI_JMICRON_CONTROLLER_CONTROL_1, 4);
96 		TRACE("Jmicron controller control 1 old 0x%08" B_PRIx32 "\n", ctrl);
97 		ctrl &= ~((1 << 9) | (1 << 12) | (1 << 14));	// disable SFF 8038i emulation
98 		ctrl |= (1 << 8) | (1 << 13) | (1 << 15);		// enable AHCI controller
99 		TRACE("Jmicron controller control 1 new 0x%08" B_PRIx32 "\n", ctrl);
100 		fPCI->write_pci_config(fPCIDevice, PCI_JMICRON_CONTROLLER_CONTROL_1, 4, ctrl);
101 	}
102 
103 	fIRQ = pciInfo.u.h0.interrupt_line;
104 	if (fIRQ == 0 || fIRQ == 0xff) {
105 		TRACE("Error: PCI IRQ not assigned\n");
106 		return B_ERROR;
107 	}
108 
109 	phys_addr_t addr = pciInfo.u.h0.base_registers[5];
110 	size_t size = pciInfo.u.h0.base_register_sizes[5];
111 
112 	TRACE("registers at %#" B_PRIxPHYSADDR ", size %#" B_PRIxSIZE "\n", addr,
113 		size);
114 	if (addr == 0) {
115 		TRACE("PCI base address register 5 not assigned\n");
116 		return B_ERROR;
117 	}
118 
119 	fRegsArea = map_mem((void **)&fRegs, addr, size, 0, "AHCI HBA regs");
120 	if (fRegsArea < B_OK) {
121 		TRACE("mapping registers failed\n");
122 		return B_ERROR;
123 	}
124 
125 	// make sure interrupts are disabled
126 	fRegs->ghc &= ~GHC_IE;
127 	FlushPostedWrites();
128 
129 	if (ResetController() < B_OK) {
130 		TRACE("controller reset failed\n");
131 		goto err;
132 	}
133 
134 	fCommandSlotCount = 1 + ((fRegs->cap >> CAP_NCS_SHIFT) & CAP_NCS_MASK);
135 	fPortCount = 1 + ((fRegs->cap >> CAP_NP_SHIFT) & CAP_NP_MASK);
136 
137 	fPortImplementedMask = fRegs->pi;
138 	// reported mask of implemented ports is sometimes empty
139 	if (fPortImplementedMask == 0) {
140 		fPortImplementedMask = 0xffffffff >> (32 - fPortCount);
141 		TRACE("ports-implemented mask is zero, using 0x%" B_PRIx32 " instead.\n",
142 			fPortImplementedMask);
143 	}
144 
145 	// reported number of ports is sometimes too small
146 	int highestPort;
147 	highestPort = fls(fPortImplementedMask); // 1-based, 1 to 32
148 	if (fPortCount < highestPort) {
149 		TRACE("reported number of ports is wrong, using %d instead.\n", highestPort);
150 		fPortCount = highestPort;
151 	}
152 
153 	TRACE("cap: Interface Speed Support: generation %" B_PRIu32 "\n",	(fRegs->cap >> CAP_ISS_SHIFT) & CAP_ISS_MASK);
154 	TRACE("cap: Number of Command Slots: %d (raw %#" B_PRIx32 ")\n",	fCommandSlotCount, (fRegs->cap >> CAP_NCS_SHIFT) & CAP_NCS_MASK);
155 	TRACE("cap: Number of Ports: %d (raw %#" B_PRIx32 ")\n",			fPortCount, (fRegs->cap >> CAP_NP_SHIFT) & CAP_NP_MASK);
156 	TRACE("cap: Supports Port Multiplier: %s\n",		(fRegs->cap & CAP_SPM) ? "yes" : "no");
157 	TRACE("cap: Supports External SATA: %s\n",			(fRegs->cap & CAP_SXS) ? "yes" : "no");
158 	TRACE("cap: Enclosure Management Supported: %s\n",	(fRegs->cap & CAP_EMS) ? "yes" : "no");
159 
160 	TRACE("cap: Supports Command List Override: %s\n",	(fRegs->cap & CAP_SCLO) ? "yes" : "no");
161 	TRACE("cap: Supports Staggered Spin-up: %s\n",	(fRegs->cap & CAP_SSS) ? "yes" : "no");
162 	TRACE("cap: Supports Mechanical Presence Switch: %s\n",	(fRegs->cap & CAP_SMPS) ? "yes" : "no");
163 
164 	TRACE("cap: Supports 64-bit Addressing: %s\n",		(fRegs->cap & CAP_S64A) ? "yes" : "no");
165 	TRACE("cap: Supports Native Command Queuing: %s\n",	(fRegs->cap & CAP_SNCQ) ? "yes" : "no");
166 	TRACE("cap: Supports SNotification Register: %s\n",	(fRegs->cap & CAP_SSNTF) ? "yes" : "no");
167 	TRACE("cap: Supports Command List Override: %s\n",	(fRegs->cap & CAP_SCLO) ? "yes" : "no");
168 
169 
170 	TRACE("cap: Supports AHCI mode only: %s\n",			(fRegs->cap & CAP_SAM) ? "yes" : "no");
171 	TRACE("ghc: AHCI Enable: %s\n",						(fRegs->ghc & GHC_AE) ? "yes" : "no");
172 	TRACE("Ports Implemented Mask: %#08" B_PRIx32 "\n",	fPortImplementedMask);
173 	TRACE("Number of Available Ports: %d\n",			count_bits_set(fPortImplementedMask));
174 	TRACE("AHCI Version %" B_PRIu32 ".%" B_PRIu32 "\n",	fRegs->vs >> 16, fRegs->vs & 0xff);
175 	TRACE("Interrupt %u\n",								fIRQ);
176 
177 	// setup interrupt handler
178 	if (install_io_interrupt_handler(fIRQ, Interrupt, this, 0) < B_OK) {
179 		TRACE("can't install interrupt handler\n");
180 		goto err;
181 	}
182 
183 	for (int i = 0; i < fPortCount; i++) {
184 		if (fPortImplementedMask & (1 << i)) {
185 			fPort[i] = new (std::nothrow)AHCIPort(this, i);
186 			if (!fPort[i]) {
187 				TRACE("out of memory creating port %d\n", i);
188 				break;
189 			}
190 			status_t status = fPort[i]->Init1();
191 			if (status < B_OK) {
192 				TRACE("init-1 port %d failed\n", i);
193 				delete fPort[i];
194 				fPort[i] = NULL;
195 			}
196 		}
197 	}
198 
199 	// clear any pending interrupts
200 	uint32 interruptsPending;
201 	interruptsPending = fRegs->is;
202 	fRegs->is = interruptsPending;
203 	FlushPostedWrites();
204 
205 	// enable interrupts
206 	fRegs->ghc |= GHC_IE;
207 	FlushPostedWrites();
208 
209 	for (int i = 0; i < fPortCount; i++) {
210 		if (fPort[i]) {
211 			status_t status = fPort[i]->Init2();
212 			if (status < B_OK) {
213 				TRACE("init-2 port %d failed\n", i);
214 				fPort[i]->Uninit();
215 				delete fPort[i];
216 				fPort[i] = NULL;
217 			}
218 		}
219 	}
220 
221 
222 	return B_OK;
223 
224 err:
225 	delete_area(fRegsArea);
226 	return B_ERROR;
227 }
228 
229 
230 void
231 AHCIController::Uninit()
232 {
233 	TRACE("AHCIController::Uninit\n");
234 
235 	for (int i = 0; i < fPortCount; i++) {
236 		if (fPort[i]) {
237 			fPort[i]->Uninit();
238 			delete fPort[i];
239 		}
240 	}
241 
242 	// disable interrupts
243 	fRegs->ghc &= ~GHC_IE;
244 	FlushPostedWrites();
245 
246 	// clear pending interrupts
247 	fRegs->is = 0xffffffff;
248 	FlushPostedWrites();
249 
250   	// well...
251   	remove_io_interrupt_handler(fIRQ, Interrupt, this);
252 
253 	delete_area(fRegsArea);
254 
255 // --- Instance check workaround begin
256 	delete_port(fInstanceCheck);
257 // --- Instance check workaround end
258 }
259 
260 
261 status_t
262 AHCIController::ResetController()
263 {
264 	uint32 saveCaps = fRegs->cap & (CAP_SMPS | CAP_SSS | CAP_SPM | CAP_EMS | CAP_SXS);
265 	uint32 savePI = fRegs->pi;
266 
267 	// AHCI 1.3: Software may perform an HBA reset prior to initializing the controller
268 	//           by setting GHC.AE to ‘1’ and then setting GHC.HR to ‘1’ if desired.
269 	fRegs->ghc |= GHC_AE;
270 	FlushPostedWrites();
271 	fRegs->ghc |= GHC_HR;
272 	FlushPostedWrites();
273 	if (wait_until_clear(&fRegs->ghc, GHC_HR, 1000000) < B_OK)
274 		return B_TIMED_OUT;
275 
276 	fRegs->ghc |= GHC_AE;
277 	FlushPostedWrites();
278 	fRegs->cap |= saveCaps;
279 	fRegs->pi = savePI;
280 	FlushPostedWrites();
281 
282 	if (fPCIVendorID == PCI_VENDOR_INTEL) {
283 		// Intel PCS—Port Control and Status
284 		// SATA port enable bits must be set
285 		int portCount = std::max(fls(fRegs->pi), 1 + (int)((fRegs->cap >> CAP_NP_SHIFT) & CAP_NP_MASK));
286 		if (portCount > 8) {
287 			// TODO: fix this when specification available
288 			TRACE("don't know how to enable SATA ports 9 to %d\n", portCount);
289 			portCount = 8;
290 		}
291 		uint16 pcs = fPCI->read_pci_config(fPCIDevice, 0x92, 2);
292 		pcs |= (0xff >> (8 - portCount));
293 		fPCI->write_pci_config(fPCIDevice, 0x92, 2, pcs);
294 	}
295 	return B_OK;
296 }
297 
298 
299 int32
300 AHCIController::Interrupt(void *data)
301 {
302 	AHCIController *self = (AHCIController *)data;
303 	uint32 interruptPending = self->fRegs->is & self->fPortImplementedMask;
304 
305 	if (interruptPending == 0)
306 		return B_UNHANDLED_INTERRUPT;
307 
308 	for (int i = 0; i < self->fPortCount; i++) {
309 		if (interruptPending & (1 << i)) {
310 			if (self->fPort[i]) {
311 				self->fPort[i]->Interrupt();
312 			} else {
313 				FLOW("interrupt on non-existent port %d\n", i);
314 			}
315 		}
316 	}
317 
318 	// clear pending interrupts
319 	self->fRegs->is = interruptPending;
320 
321 	return B_INVOKE_SCHEDULER;
322 }
323 
324 
325 void
326 AHCIController::ExecuteRequest(scsi_ccb *request)
327 {
328 	if (request->target_lun || !fPort[request->target_id]) {
329 		request->subsys_status = SCSI_DEV_NOT_THERE;
330 		gSCSI->finished(request, 1);
331 		return;
332 	}
333 
334 	fPort[request->target_id]->ScsiExecuteRequest(request);
335 }
336 
337 
338 uchar
339 AHCIController::AbortRequest(scsi_ccb *request)
340 {
341 	if (request->target_lun || !fPort[request->target_id])
342 		return SCSI_DEV_NOT_THERE;
343 
344 	return fPort[request->target_id]->ScsiAbortRequest(request);
345 }
346 
347 
348 uchar
349 AHCIController::TerminateRequest(scsi_ccb *request)
350 {
351 	if (request->target_lun || !fPort[request->target_id])
352 		return SCSI_DEV_NOT_THERE;
353 
354 	return fPort[request->target_id]->ScsiTerminateRequest(request);
355 }
356 
357 
358 uchar
359 AHCIController::ResetDevice(uchar targetID, uchar targetLUN)
360 {
361 	if (targetLUN || !fPort[targetID])
362 		return SCSI_DEV_NOT_THERE;
363 
364 	return fPort[targetID]->ScsiResetDevice();
365 }
366 
367 
368 void
369 AHCIController::GetRestrictions(uchar targetID, bool *isATAPI,
370 	bool *noAutoSense, uint32 *maxBlocks)
371 {
372 	if (!fPort[targetID])
373 		return;
374 
375 	fPort[targetID]->ScsiGetRestrictions(isATAPI, noAutoSense, maxBlocks);
376 }
377