xref: /haiku/src/add-ons/kernel/busses/scsi/ahci/ahci_controller.cpp (revision 079eccf655ba39812b421ae1b87a727d41b50354)
1 /*
2  * Copyright 2007, 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 <KernelExport.h>
10 #include <stdio.h>
11 #include <string.h>
12 #include <new>
13 
14 #define TRACE(a...) dprintf("\33[34mahci:\33[0m " a)
15 #define FLOW(a...)	dprintf("ahci: " a)
16 
17 
18 AHCIController::AHCIController(device_node *node,
19 		pci_device_module_info *pciModule, pci_device *device)
20 	:
21 	fNode(node),
22 	fPCI(pciModule),
23 	fPCIDevice(device),
24 	fPCIVendorID(0xffff),
25 	fPCIDeviceID(0xffff),
26 	fFlags(0),
27 	fCommandSlotCount(0),
28 	fPortCountMax(0),
29 	fPortCountAvail(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%08lx, satacr1 = 0x%08lx\n", satacr0, satacr1);
84 	}
85 
86 	uint16 pcicmd = fPCI->read_pci_config(fPCIDevice, PCI_command, 2);
87 	TRACE("pcicmd old 0x%04x\n", pcicmd);
88 	pcicmd &= ~(PCI_command_io | PCI_command_int_disable);
89 	pcicmd |= PCI_command_master | PCI_command_memory;
90 	TRACE("pcicmd new 0x%04x\n", pcicmd);
91 	fPCI->write_pci_config(fPCIDevice, PCI_command, 2, pcicmd);
92 
93 	if (fPCIVendorID == PCI_VENDOR_JMICRON) {
94 		uint32 ctrl = fPCI->read_pci_config(fPCIDevice, PCI_JMICRON_CONTROLLER_CONTROL_1, 4);
95 		TRACE("Jmicron controller control 1 old 0x%08lx\n", ctrl);
96 		ctrl &= ~((1 << 9) | (1 << 12) | (1 << 14));	// disable SFF 8038i emulation
97 		ctrl |= (1 << 8) | (1 << 13) | (1 << 15);		// enable AHCI controller
98 		TRACE("Jmicron controller control 1 new 0x%08lx\n", ctrl);
99 		fPCI->write_pci_config(fPCIDevice, PCI_JMICRON_CONTROLLER_CONTROL_1, 4, ctrl);
100 	}
101 
102 	fIRQ = pciInfo.u.h0.interrupt_line;
103 	if (fIRQ == 0 || fIRQ == 0xff) {
104 		TRACE("Error: PCI IRQ not assigned\n");
105 		return B_ERROR;
106 	}
107 
108 	void *addr = (void *)pciInfo.u.h0.base_registers[5];
109 	size_t size = pciInfo.u.h0.base_register_sizes[5];
110 
111 	TRACE("registers at %p, size %#lx\n", addr, size);
112 	if (!addr) {
113 		TRACE("PCI base address register 5 not assigned\n");
114 		return B_ERROR;
115 	}
116 
117 	fRegsArea = map_mem((void **)&fRegs, addr, size, 0, "AHCI HBA regs");
118 	if (fRegsArea < B_OK) {
119 		TRACE("mapping registers failed\n");
120 		return B_ERROR;
121 	}
122 
123 	if (ResetController() < B_OK) {
124 		TRACE("controller reset failed\n");
125 		goto err;
126 	}
127 
128 	fCommandSlotCount = 1 + ((fRegs->cap >> CAP_NCS_SHIFT) & CAP_NCS_MASK);
129 	fPortCountMax = 1 + ((fRegs->cap >> CAP_NP_SHIFT) & CAP_NP_MASK);
130 
131 	fPortImplementedMask = fRegs->pi;
132 	if (fPortImplementedMask == 0) {
133 		fPortImplementedMask = 0xffffffff >> (32 - fPortCountMax);
134 		TRACE("ports-implemented mask is zero, using 0x%lx instead.\n", fPortImplementedMask);
135 	}
136 
137 	fPortCountAvail = count_bits_set(fPortImplementedMask);
138 
139 	TRACE("cap: Interface Speed Support: generation %lu\n",	(fRegs->cap >> CAP_ISS_SHIFT) & CAP_ISS_MASK);
140 	TRACE("cap: Number of Command Slots: %d (raw %#lx)\n",	fCommandSlotCount, (fRegs->cap >> CAP_NCS_SHIFT) & CAP_NCS_MASK);
141 	TRACE("cap: Number of Ports: %d (raw %#lx)\n",			fPortCountMax, (fRegs->cap >> CAP_NP_SHIFT) & CAP_NP_MASK);
142 	TRACE("cap: Supports Port Multiplier: %s\n",		(fRegs->cap & CAP_SPM) ? "yes" : "no");
143 	TRACE("cap: Supports External SATA: %s\n",			(fRegs->cap & CAP_SXS) ? "yes" : "no");
144 	TRACE("cap: Enclosure Management Supported: %s\n",	(fRegs->cap & CAP_EMS) ? "yes" : "no");
145 
146 	TRACE("cap: Supports Command List Override: %s\n",	(fRegs->cap & CAP_SCLO) ? "yes" : "no");
147 	TRACE("cap: Supports Staggered Spin-up: %s\n",	(fRegs->cap & CAP_SSS) ? "yes" : "no");
148 	TRACE("cap: Supports Mechanical Presence Switch: %s\n",	(fRegs->cap & CAP_SMPS) ? "yes" : "no");
149 
150 	TRACE("cap: Supports 64-bit Addressing: %s\n",		(fRegs->cap & CAP_S64A) ? "yes" : "no");
151 	TRACE("cap: Supports Native Command Queuing: %s\n",	(fRegs->cap & CAP_SNCQ) ? "yes" : "no");
152 	TRACE("cap: Supports SNotification Register: %s\n",	(fRegs->cap & CAP_SSNTF) ? "yes" : "no");
153 	TRACE("cap: Supports Command List Override: %s\n",	(fRegs->cap & CAP_SCLO) ? "yes" : "no");
154 
155 
156 	TRACE("cap: Supports AHCI mode only: %s\n",			(fRegs->cap & CAP_SAM) ? "yes" : "no");
157 	TRACE("ghc: AHCI Enable: %s\n",						(fRegs->ghc & GHC_AE) ? "yes" : "no");
158 	TRACE("Ports Implemented Mask: %#08lx\n",			fPortImplementedMask);
159 	TRACE("Number of Available Ports: %d\n",			fPortCountAvail);
160 	TRACE("AHCI Version %lu.%lu\n",						fRegs->vs >> 16, fRegs->vs & 0xff);
161 	TRACE("Interrupt %u\n",								fIRQ);
162 
163 	// setup interrupt handler
164 	if (install_io_interrupt_handler(fIRQ, Interrupt, this, 0) < B_OK) {
165 		TRACE("can't install interrupt handler\n");
166 		goto err;
167 	}
168 
169 	for (int i = 0; i <= fPortCountMax; i++) {
170 		if (fPortImplementedMask & (1 << i)) {
171 			fPort[i] = new (std::nothrow)AHCIPort(this, i);
172 			if (!fPort[i]) {
173 				TRACE("out of memory creating port %d", i);
174 				break;
175 			}
176 			status_t status = fPort[i]->Init1();
177 			if (status < B_OK) {
178 				TRACE("init-1 port %d failed", i);
179 				delete fPort[i];
180 				fPort[i] = NULL;
181 			}
182 		}
183 	}
184 
185 	// enable interrupts
186 	fRegs->ghc |= GHC_IE;
187 	FlushPostedWrites();
188 
189 	for (int i = 0; i <= fPortCountMax; i++) {
190 		if (fPort[i]) {
191 			status_t status = fPort[i]->Init2();
192 			if (status < B_OK) {
193 				TRACE("init-2 port %d failed", i);
194 				fPort[i]->Uninit();
195 				delete fPort[i];
196 				fPort[i] = NULL;
197 			}
198 		}
199 	}
200 
201 
202 	return B_OK;
203 
204 err:
205 	delete_area(fRegsArea);
206 	return B_ERROR;
207 }
208 
209 
210 void
211 AHCIController::Uninit()
212 {
213 	TRACE("AHCIController::Uninit\n");
214 
215 	for (int i = 0; i <= fPortCountMax; i++) {
216 		if (fPort[i]) {
217 			fPort[i]->Uninit();
218 			delete fPort[i];
219 		}
220 	}
221 
222 	// disable interrupts
223 	fRegs->ghc &= ~GHC_IE;
224 	FlushPostedWrites();
225 
226 	// clear pending interrupts
227 	fRegs->is = 0xffffffff;
228 	FlushPostedWrites();
229 
230   	// well...
231   	remove_io_interrupt_handler(fIRQ, Interrupt, this);
232 
233 	delete_area(fRegsArea);
234 
235 // --- Instance check workaround begin
236 	delete_port(fInstanceCheck);
237 // --- Instance check workaround end
238 }
239 
240 
241 status_t
242 AHCIController::ResetController()
243 {
244 	uint32 saveCaps = fRegs->cap & (CAP_SMPS | CAP_SSS | CAP_SPM | CAP_EMS | CAP_SXS);
245 	uint32 savePI = fRegs->pi;
246 
247 	fRegs->ghc |= GHC_HR;
248 	FlushPostedWrites();
249 	if (wait_until_clear(&fRegs->ghc, GHC_HR, 1000000) < B_OK)
250 		return B_TIMED_OUT;
251 
252 	fRegs->ghc |= GHC_AE;
253 	FlushPostedWrites();
254 	fRegs->cap |= saveCaps;
255 	fRegs->pi = savePI;
256 	FlushPostedWrites();
257 
258 	if (fPCIVendorID == PCI_VENDOR_INTEL) {
259 		// Intel PCS—Port Control and Status
260 		// SATA port enable bits must be set
261 		int portCount = 1 + ((fRegs->cap >> CAP_NP_SHIFT) & CAP_NP_MASK);
262 		if (portCount > 8)
263 			panic("Intel AHCI: too many SATA ports! Please report at http://dev.haiku-os.org");
264 		uint16 pcs = fPCI->read_pci_config(fPCIDevice, 0x92, 2);
265 		pcs |= (0xff >> (8 - portCount));
266 		fPCI->write_pci_config(fPCIDevice, 0x92, 2, pcs);
267 	}
268 	return B_OK;
269 }
270 
271 
272 int32
273 AHCIController::Interrupt(void *data)
274 {
275 	AHCIController *self = (AHCIController *)data;
276 	uint32 interruptPending = self->fRegs->is & self->fPortImplementedMask;
277 
278 	if (interruptPending == 0)
279 		return B_UNHANDLED_INTERRUPT;
280 
281 	for (int i = 0; i < self->fPortCountMax; i++) {
282 		if (interruptPending & (1 << i)) {
283 			if (self->fPort[i]) {
284 				self->fPort[i]->Interrupt();
285 			} else {
286 				FLOW("interrupt on non-existent port %d\n", i);
287 			}
288 		}
289 	}
290 
291 	// clear pending interrupts
292 	self->fRegs->is = interruptPending;
293 
294 	return B_INVOKE_SCHEDULER;
295 }
296 
297 
298 void
299 AHCIController::ExecuteRequest(scsi_ccb *request)
300 {
301 	if (request->target_lun || !fPort[request->target_id]) {
302 		request->subsys_status = SCSI_DEV_NOT_THERE;
303 		gSCSI->finished(request, 1);
304 		return;
305 	}
306 
307 	fPort[request->target_id]->ScsiExecuteRequest(request);
308 }
309 
310 
311 uchar
312 AHCIController::AbortRequest(scsi_ccb *request)
313 {
314 	if (request->target_lun || !fPort[request->target_id])
315 		return SCSI_DEV_NOT_THERE;
316 
317 	return fPort[request->target_id]->ScsiAbortRequest(request);
318 }
319 
320 
321 uchar
322 AHCIController::TerminateRequest(scsi_ccb *request)
323 {
324 	if (request->target_lun || !fPort[request->target_id])
325 		return SCSI_DEV_NOT_THERE;
326 
327 	return fPort[request->target_id]->ScsiTerminateRequest(request);
328 }
329 
330 
331 uchar
332 AHCIController::ResetDevice(uchar targetID, uchar targetLUN)
333 {
334 	if (targetLUN || !fPort[targetID])
335 		return SCSI_DEV_NOT_THERE;
336 
337 	return fPort[targetID]->ScsiResetDevice();
338 }
339 
340 
341 void
342 AHCIController::GetRestrictions(uchar targetID, bool *isATAPI,
343 	bool *noAutoSense, uint32 *maxBlocks)
344 {
345 	if (!fPort[targetID])
346 		return;
347 
348 	fPort[targetID]->ScsiGetRestrictions(isATAPI, noAutoSense, maxBlocks);
349 }
350