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