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