xref: /minix3/minix/drivers/storage/ahci/ahci.c (revision 433d6423c39e34ec4b79c950597bb2d236f886be)
1*433d6423SLionel Sambuc /* Advanced Host Controller Interface (AHCI) driver, by D.C. van Moolenbroek
2*433d6423SLionel Sambuc  * - Multithreading support by Arne Welzel
3*433d6423SLionel Sambuc  * - Native Command Queuing support by Raja Appuswamy
4*433d6423SLionel Sambuc  */
5*433d6423SLionel Sambuc /*
6*433d6423SLionel Sambuc  * This driver is based on the following specifications:
7*433d6423SLionel Sambuc  * - Serial ATA Advanced Host Controller Interface (AHCI) 1.3
8*433d6423SLionel Sambuc  * - Serial ATA Revision 2.6
9*433d6423SLionel Sambuc  * - AT Attachment with Packet Interface 7 (ATA/ATAPI-7)
10*433d6423SLionel Sambuc  * - ATAPI Removable Rewritable Media Devices 1.3 (SFF-8070)
11*433d6423SLionel Sambuc  *
12*433d6423SLionel Sambuc  * The driver supports device hot-plug, active device status tracking,
13*433d6423SLionel Sambuc  * nonremovable ATA and removable ATAPI devices, custom logical sector sizes,
14*433d6423SLionel Sambuc  * sector-unaligned reads, native command queuing and parallel requests to
15*433d6423SLionel Sambuc  * different devices.
16*433d6423SLionel Sambuc  *
17*433d6423SLionel Sambuc  * It does not implement transparent failure recovery, power management, or
18*433d6423SLionel Sambuc  * port multiplier support.
19*433d6423SLionel Sambuc  */
20*433d6423SLionel Sambuc /*
21*433d6423SLionel Sambuc  * An AHCI controller exposes a number of ports (up to 32), each of which may
22*433d6423SLionel Sambuc  * or may not have one device attached (port multipliers are not supported).
23*433d6423SLionel Sambuc  * Each port is maintained independently.
24*433d6423SLionel Sambuc  *
25*433d6423SLionel Sambuc  * The following figure depicts the possible transitions between port states.
26*433d6423SLionel Sambuc  * The NO_PORT state is not included; no transitions can be made from or to it.
27*433d6423SLionel Sambuc  *
28*433d6423SLionel Sambuc  *   +----------+                      +----------+
29*433d6423SLionel Sambuc  *   | SPIN_UP  | ------+      +-----> | BAD_DEV  | ------------------+
30*433d6423SLionel Sambuc  *   +----------+       |      |       +----------+                   |
31*433d6423SLionel Sambuc  *        |             |      |            ^                         |
32*433d6423SLionel Sambuc  *        v             v      |            |                         |
33*433d6423SLionel Sambuc  *   +----------+     +----------+     +----------+     +----------+  |
34*433d6423SLionel Sambuc  *   |  NO_DEV  | --> | WAIT_DEV | --> | WAIT_ID  | --> | GOOD_DEV |  |
35*433d6423SLionel Sambuc  *   +----------+     +----------+     +----------+     +----------+  |
36*433d6423SLionel Sambuc  *        ^                |                |                |        |
37*433d6423SLionel Sambuc  *        +----------------+----------------+----------------+--------+
38*433d6423SLionel Sambuc  *
39*433d6423SLionel Sambuc  * At driver startup, all physically present ports are put in SPIN_UP state.
40*433d6423SLionel Sambuc  * This state differs from NO_DEV in that BDEV_OPEN calls will be deferred
41*433d6423SLionel Sambuc  * until either the spin-up timer expires, or a device has been identified on
42*433d6423SLionel Sambuc  * that port. This prevents early BDEV_OPEN calls from failing erroneously at
43*433d6423SLionel Sambuc  * startup time if the device has not yet been able to announce its presence.
44*433d6423SLionel Sambuc  *
45*433d6423SLionel Sambuc  * If a device is detected, either at startup time or after hot-plug, its
46*433d6423SLionel Sambuc  * signature is checked and it is identified, after which it may be determined
47*433d6423SLionel Sambuc  * to be a usable ("good") device, which means that the device is considered to
48*433d6423SLionel Sambuc  * be in a working state. If these steps fail, the device is marked as unusable
49*433d6423SLionel Sambuc  * ("bad"). At any point in time, the device may be disconnected; the port is
50*433d6423SLionel Sambuc  * then put back into NO_DEV state.
51*433d6423SLionel Sambuc  *
52*433d6423SLionel Sambuc  * A device in working state (GOOD_DEV) may or may not have a medium. All ATA
53*433d6423SLionel Sambuc  * devices are assumed to be fixed; all ATAPI devices are assumed to have
54*433d6423SLionel Sambuc  * removable media. To prevent erroneous access to switched devices and media,
55*433d6423SLionel Sambuc  * the driver makes devices inaccessible until they are fully closed (the open
56*433d6423SLionel Sambuc  * count is zero) when a device (hot-plug) or medium change is detected.
57*433d6423SLionel Sambuc  * For hot-plug changes, access is prevented by setting the BARRIER flag until
58*433d6423SLionel Sambuc  * the device is fully closed and then reopened. For medium changes, access is
59*433d6423SLionel Sambuc  * prevented by not acknowledging the medium change until the device is fully
60*433d6423SLionel Sambuc  * closed and reopened. Removable media are not locked in the drive while
61*433d6423SLionel Sambuc  * opened, because the driver author is uncomfortable with that concept.
62*433d6423SLionel Sambuc  *
63*433d6423SLionel Sambuc  * Ports may leave the group of states where a device is connected (that is,
64*433d6423SLionel Sambuc  * WAIT_ID, GOOD_DEV, and BAD_DEV) in two ways: either due to a hot-unplug
65*433d6423SLionel Sambuc  * event, or due to a hard reset after a serious failure. For simplicity, we
66*433d6423SLionel Sambuc  * we perform a hard reset after a hot-unplug event as well, so that the link
67*433d6423SLionel Sambuc  * to the device is broken. Thus, in both cases, a transition to NO_DEV is
68*433d6423SLionel Sambuc  * made, after which the link to the device may or may not be reestablished.
69*433d6423SLionel Sambuc  * In both cases, ongoing requests are cancelled and the BARRIER flag is set.
70*433d6423SLionel Sambuc  *
71*433d6423SLionel Sambuc  * The following table lists for each state, whether the port is started
72*433d6423SLionel Sambuc  * (PxCMD.ST is set), whether a timer is running, what the PxIE mask is to be
73*433d6423SLionel Sambuc  * set to, and what BDEV_OPEN calls on this port should return.
74*433d6423SLionel Sambuc  *
75*433d6423SLionel Sambuc  *   State       Started     Timer       PxIE        BDEV_OPEN
76*433d6423SLionel Sambuc  *   ---------   ---------   ---------   ---------   ---------
77*433d6423SLionel Sambuc  *   NO_PORT     no          no          (none)      ENXIO
78*433d6423SLionel Sambuc  *   SPIN_UP     no          yes         PCE         (wait)
79*433d6423SLionel Sambuc  *   NO_DEV      no          no          PCE         ENXIO
80*433d6423SLionel Sambuc  *   WAIT_DEV    no          yes         PCE         (wait)
81*433d6423SLionel Sambuc  *   BAD_DEV     no          no          PRCE        ENXIO
82*433d6423SLionel Sambuc  *   WAIT_ID     yes         yes         PRCE+       (wait)
83*433d6423SLionel Sambuc  *   GOOD_DEV    yes         per-command PRCE+       OK
84*433d6423SLionel Sambuc  *
85*433d6423SLionel Sambuc  * In order to continue deferred BDEV_OPEN calls, the BUSY flag must be unset
86*433d6423SLionel Sambuc  * when changing from SPIN_UP to any state but WAIT_DEV, and when changing from
87*433d6423SLionel Sambuc  * WAIT_DEV to any state but WAIT_ID, and when changing from WAIT_ID to any
88*433d6423SLionel Sambuc  * other state.
89*433d6423SLionel Sambuc  */
90*433d6423SLionel Sambuc /*
91*433d6423SLionel Sambuc  * The maximum byte size of a single transfer (MAX_TRANSFER) is currently set
92*433d6423SLionel Sambuc  * to 4MB. This limit has been chosen for a number of reasons:
93*433d6423SLionel Sambuc  * - The size that can be specified in a Physical Region Descriptor (PRD) is
94*433d6423SLionel Sambuc  *   limited to 4MB for AHCI. Limiting the total transfer size to at most this
95*433d6423SLionel Sambuc  *   size implies that no I/O vector element needs to be split up across PRDs.
96*433d6423SLionel Sambuc  *   This means that the maximum number of needed PRDs can be predetermined.
97*433d6423SLionel Sambuc  * - The limit is below what can be transferred in a single ATA request, namely
98*433d6423SLionel Sambuc  *   64k sectors (i.e., at least 32MB). This means that transfer requests need
99*433d6423SLionel Sambuc  *   never be split up into smaller chunks, reducing implementation complexity.
100*433d6423SLionel Sambuc  * - A single, static timeout can be used for transfers. Very large transfers
101*433d6423SLionel Sambuc  *   can legitimately take up to several minutes -- well beyond the appropriate
102*433d6423SLionel Sambuc  *   timeout range for small transfers. The limit obviates the need for a
103*433d6423SLionel Sambuc  *   timeout scheme that takes into account the transfer size.
104*433d6423SLionel Sambuc  * - Similarly, the transfer limit reduces the opportunity for buggy/malicious
105*433d6423SLionel Sambuc  *   clients to keep the driver busy for a long time with a single request.
106*433d6423SLionel Sambuc  * - The limit is high enough for all practical purposes. The transfer setup
107*433d6423SLionel Sambuc  *   overhead is already relatively negligible at this size, and even larger
108*433d6423SLionel Sambuc  *   requests will not help maximize throughput. As NR_IOREQS is currently set
109*433d6423SLionel Sambuc  *   to 64, the limit still allows file systems to perform I/O requests with
110*433d6423SLionel Sambuc  *   vectors completely filled with 64KB-blocks.
111*433d6423SLionel Sambuc  */
112*433d6423SLionel Sambuc #include <minix/drivers.h>
113*433d6423SLionel Sambuc #include <minix/blockdriver_mt.h>
114*433d6423SLionel Sambuc #include <minix/drvlib.h>
115*433d6423SLionel Sambuc #include <machine/pci.h>
116*433d6423SLionel Sambuc #include <sys/ioc_disk.h>
117*433d6423SLionel Sambuc #include <sys/mman.h>
118*433d6423SLionel Sambuc #include <assert.h>
119*433d6423SLionel Sambuc 
120*433d6423SLionel Sambuc #include "ahci.h"
121*433d6423SLionel Sambuc 
122*433d6423SLionel Sambuc /* Host Bus Adapter (HBA) state. */
123*433d6423SLionel Sambuc static struct {
124*433d6423SLionel Sambuc 	volatile u32_t *base;	/* base address of memory-mapped registers */
125*433d6423SLionel Sambuc 	size_t size;		/* size of memory-mapped register area */
126*433d6423SLionel Sambuc 
127*433d6423SLionel Sambuc 	int nr_ports;		/* addressable number of ports (1..NR_PORTS) */
128*433d6423SLionel Sambuc 	int nr_cmds;		/* maximum number of commands per port */
129*433d6423SLionel Sambuc 	int has_ncq;		/* NCQ support flag */
130*433d6423SLionel Sambuc 	int has_clo;		/* CLO support flag */
131*433d6423SLionel Sambuc 
132*433d6423SLionel Sambuc 	int irq;		/* IRQ number */
133*433d6423SLionel Sambuc 	int hook_id;		/* IRQ hook ID */
134*433d6423SLionel Sambuc } hba_state;
135*433d6423SLionel Sambuc 
136*433d6423SLionel Sambuc #define hba_read(r)		(hba_state.base[r])
137*433d6423SLionel Sambuc #define hba_write(r, v)		(hba_state.base[r] = (v))
138*433d6423SLionel Sambuc 
139*433d6423SLionel Sambuc /* Port state. */
140*433d6423SLionel Sambuc static struct port_state {
141*433d6423SLionel Sambuc 	int state;		/* port state */
142*433d6423SLionel Sambuc 	unsigned int flags;	/* port flags */
143*433d6423SLionel Sambuc 
144*433d6423SLionel Sambuc 	volatile u32_t *reg;	/* memory-mapped port registers */
145*433d6423SLionel Sambuc 
146*433d6423SLionel Sambuc 	u8_t *mem_base;		/* primary memory buffer virtual address */
147*433d6423SLionel Sambuc 	phys_bytes mem_phys;	/* primary memory buffer physical address */
148*433d6423SLionel Sambuc 	vir_bytes mem_size;	/* primary memory buffer size */
149*433d6423SLionel Sambuc 
150*433d6423SLionel Sambuc 	/* the FIS, CL, CT[0] and TMP buffers are all in the primary buffer */
151*433d6423SLionel Sambuc 	u32_t *fis_base;	/* FIS receive buffer virtual address */
152*433d6423SLionel Sambuc 	phys_bytes fis_phys;	/* FIS receive buffer physical address */
153*433d6423SLionel Sambuc 	u32_t *cl_base;		/* command list buffer virtual address */
154*433d6423SLionel Sambuc 	phys_bytes cl_phys;	/* command list buffer physical address */
155*433d6423SLionel Sambuc 	u8_t *ct_base[NR_CMDS];	/* command table virtual address */
156*433d6423SLionel Sambuc 	phys_bytes ct_phys[NR_CMDS];	/* command table physical address */
157*433d6423SLionel Sambuc 	u8_t *tmp_base;		/* temporary storage buffer virtual address */
158*433d6423SLionel Sambuc 	phys_bytes tmp_phys;	/* temporary storage buffer physical address */
159*433d6423SLionel Sambuc 
160*433d6423SLionel Sambuc 	u8_t *pad_base;		/* sector padding buffer virtual address */
161*433d6423SLionel Sambuc 	phys_bytes pad_phys;	/* sector padding buffer physical address */
162*433d6423SLionel Sambuc 	vir_bytes pad_size;	/* sector padding buffer size */
163*433d6423SLionel Sambuc 
164*433d6423SLionel Sambuc 	u64_t lba_count;	/* number of valid Logical Block Addresses */
165*433d6423SLionel Sambuc 	u32_t sector_size;	/* medium sector size in bytes */
166*433d6423SLionel Sambuc 
167*433d6423SLionel Sambuc 	int open_count;		/* number of times this port is opened */
168*433d6423SLionel Sambuc 
169*433d6423SLionel Sambuc 	int device;		/* associated device number, or NO_DEVICE */
170*433d6423SLionel Sambuc 	struct device part[DEV_PER_DRIVE];	/* partition bases and sizes */
171*433d6423SLionel Sambuc 	struct device subpart[SUB_PER_DRIVE];	/* same for subpartitions */
172*433d6423SLionel Sambuc 
173*433d6423SLionel Sambuc 	minix_timer_t timer;		/* port-specific timeout timer */
174*433d6423SLionel Sambuc 	int left;		/* number of tries left before giving up */
175*433d6423SLionel Sambuc 				/* (only used for signature probing) */
176*433d6423SLionel Sambuc 
177*433d6423SLionel Sambuc 	int queue_depth;	/* NCQ queue depth */
178*433d6423SLionel Sambuc 	u32_t pend_mask;	/* commands not yet complete */
179*433d6423SLionel Sambuc 	struct {
180*433d6423SLionel Sambuc 		thread_id_t tid;/* ID of the worker thread */
181*433d6423SLionel Sambuc 		minix_timer_t timer;	/* timer associated with each request */
182*433d6423SLionel Sambuc 		int result;	/* success/failure result of the commands */
183*433d6423SLionel Sambuc 	} cmd_info[NR_CMDS];
184*433d6423SLionel Sambuc } port_state[NR_PORTS];
185*433d6423SLionel Sambuc 
186*433d6423SLionel Sambuc #define port_read(ps, r)	((ps)->reg[r])
187*433d6423SLionel Sambuc #define port_write(ps, r, v)	((ps)->reg[r] = (v))
188*433d6423SLionel Sambuc 
189*433d6423SLionel Sambuc static int ahci_instance;			/* driver instance number */
190*433d6423SLionel Sambuc 
191*433d6423SLionel Sambuc static int ahci_verbose;			/* verbosity level (0..4) */
192*433d6423SLionel Sambuc 
193*433d6423SLionel Sambuc /* Timeout-related values. */
194*433d6423SLionel Sambuc static clock_t ahci_spinup_timeout;
195*433d6423SLionel Sambuc static clock_t ahci_device_timeout;
196*433d6423SLionel Sambuc static clock_t ahci_device_delay;
197*433d6423SLionel Sambuc static unsigned int ahci_device_checks;
198*433d6423SLionel Sambuc static clock_t ahci_command_timeout;
199*433d6423SLionel Sambuc static clock_t ahci_transfer_timeout;
200*433d6423SLionel Sambuc static clock_t ahci_flush_timeout;
201*433d6423SLionel Sambuc 
202*433d6423SLionel Sambuc /* Timeout environment variable names and default values. */
203*433d6423SLionel Sambuc static struct {
204*433d6423SLionel Sambuc 	char *name;				/* environment variable name */
205*433d6423SLionel Sambuc 	u32_t default_ms;			/* default in milliseconds */
206*433d6423SLionel Sambuc 	clock_t *ptr;				/* clock ticks value pointer */
207*433d6423SLionel Sambuc } ahci_timevar[] = {
208*433d6423SLionel Sambuc 	{ "ahci_init_timeout",   SPINUP_TIMEOUT,    &ahci_spinup_timeout   },
209*433d6423SLionel Sambuc 	{ "ahci_device_timeout", DEVICE_TIMEOUT,    &ahci_device_timeout   },
210*433d6423SLionel Sambuc 	{ "ahci_cmd_timeout",    COMMAND_TIMEOUT,   &ahci_command_timeout  },
211*433d6423SLionel Sambuc 	{ "ahci_io_timeout",     TRANSFER_TIMEOUT,  &ahci_transfer_timeout },
212*433d6423SLionel Sambuc 	{ "ahci_flush_timeout",  FLUSH_TIMEOUT,     &ahci_flush_timeout    }
213*433d6423SLionel Sambuc };
214*433d6423SLionel Sambuc 
215*433d6423SLionel Sambuc static int ahci_map[MAX_DRIVES];		/* device-to-port mapping */
216*433d6423SLionel Sambuc 
217*433d6423SLionel Sambuc static int ahci_exiting = FALSE;		/* exit after last close? */
218*433d6423SLionel Sambuc 
219*433d6423SLionel Sambuc #define BUILD_ARG(port, tag)	(((port) << 8) | (tag))
220*433d6423SLionel Sambuc #define GET_PORT(arg)		((arg) >> 8)
221*433d6423SLionel Sambuc #define GET_TAG(arg)		((arg) & 0xFF)
222*433d6423SLionel Sambuc 
223*433d6423SLionel Sambuc #define dprintf(v,s) do {		\
224*433d6423SLionel Sambuc 	if (ahci_verbose >= (v))	\
225*433d6423SLionel Sambuc 		printf s;		\
226*433d6423SLionel Sambuc } while (0)
227*433d6423SLionel Sambuc 
228*433d6423SLionel Sambuc /* Convert milliseconds to clock ticks. Round up. */
229*433d6423SLionel Sambuc #define millis_to_hz(ms)	(((ms) * sys_hz() + 999) / 1000)
230*433d6423SLionel Sambuc 
231*433d6423SLionel Sambuc static void port_set_cmd(struct port_state *ps, int cmd, cmd_fis_t *fis,
232*433d6423SLionel Sambuc 	u8_t packet[ATAPI_PACKET_SIZE], prd_t *prdt, int nr_prds, int write);
233*433d6423SLionel Sambuc static void port_issue(struct port_state *ps, int cmd, clock_t timeout);
234*433d6423SLionel Sambuc static int port_exec(struct port_state *ps, int cmd, clock_t timeout);
235*433d6423SLionel Sambuc static void port_timeout(minix_timer_t *tp);
236*433d6423SLionel Sambuc static void port_disconnect(struct port_state *ps);
237*433d6423SLionel Sambuc 
238*433d6423SLionel Sambuc static char *ahci_portname(struct port_state *ps);
239*433d6423SLionel Sambuc static int ahci_open(devminor_t minor, int access);
240*433d6423SLionel Sambuc static int ahci_close(devminor_t minor);
241*433d6423SLionel Sambuc static ssize_t ahci_transfer(devminor_t minor, int do_write, u64_t position,
242*433d6423SLionel Sambuc 	endpoint_t endpt, iovec_t *iovec, unsigned int count, int flags);
243*433d6423SLionel Sambuc static struct device *ahci_part(devminor_t minor);
244*433d6423SLionel Sambuc static void ahci_alarm(clock_t stamp);
245*433d6423SLionel Sambuc static int ahci_ioctl(devminor_t minor, unsigned long request,
246*433d6423SLionel Sambuc 	endpoint_t endpt, cp_grant_id_t grant, endpoint_t user_endpt);
247*433d6423SLionel Sambuc static void ahci_intr(unsigned int mask);
248*433d6423SLionel Sambuc static int ahci_device(devminor_t minor, device_id_t *id);
249*433d6423SLionel Sambuc static struct port_state *ahci_get_port(devminor_t minor);
250*433d6423SLionel Sambuc 
251*433d6423SLionel Sambuc /* AHCI driver table. */
252*433d6423SLionel Sambuc static struct blockdriver ahci_dtab = {
253*433d6423SLionel Sambuc 	.bdr_type	= BLOCKDRIVER_TYPE_DISK,
254*433d6423SLionel Sambuc 	.bdr_open	= ahci_open,
255*433d6423SLionel Sambuc 	.bdr_close	= ahci_close,
256*433d6423SLionel Sambuc 	.bdr_transfer	= ahci_transfer,
257*433d6423SLionel Sambuc 	.bdr_ioctl	= ahci_ioctl,
258*433d6423SLionel Sambuc 	.bdr_part	= ahci_part,
259*433d6423SLionel Sambuc 	.bdr_intr	= ahci_intr,
260*433d6423SLionel Sambuc 	.bdr_alarm	= ahci_alarm,
261*433d6423SLionel Sambuc 	.bdr_device	= ahci_device
262*433d6423SLionel Sambuc };
263*433d6423SLionel Sambuc 
264*433d6423SLionel Sambuc /*===========================================================================*
265*433d6423SLionel Sambuc  *				atapi_exec				     *
266*433d6423SLionel Sambuc  *===========================================================================*/
267*433d6423SLionel Sambuc static int atapi_exec(struct port_state *ps, int cmd,
268*433d6423SLionel Sambuc 	u8_t packet[ATAPI_PACKET_SIZE], size_t size, int write)
269*433d6423SLionel Sambuc {
270*433d6423SLionel Sambuc 	/* Execute an ATAPI command. Return OK or error.
271*433d6423SLionel Sambuc 	 */
272*433d6423SLionel Sambuc 	cmd_fis_t fis;
273*433d6423SLionel Sambuc 	prd_t prd[1];
274*433d6423SLionel Sambuc 	int nr_prds = 0;
275*433d6423SLionel Sambuc 
276*433d6423SLionel Sambuc 	assert(size <= AHCI_TMP_SIZE);
277*433d6423SLionel Sambuc 
278*433d6423SLionel Sambuc 	/* Fill in the command table with a FIS, a packet, and if a data
279*433d6423SLionel Sambuc 	 * transfer is requested, also a PRD.
280*433d6423SLionel Sambuc 	 */
281*433d6423SLionel Sambuc 	memset(&fis, 0, sizeof(fis));
282*433d6423SLionel Sambuc 	fis.cf_cmd = ATA_CMD_PACKET;
283*433d6423SLionel Sambuc 
284*433d6423SLionel Sambuc 	if (size > 0) {
285*433d6423SLionel Sambuc 		fis.cf_feat = ATA_FEAT_PACKET_DMA;
286*433d6423SLionel Sambuc 		if (!write && (ps->flags & FLAG_USE_DMADIR))
287*433d6423SLionel Sambuc 			fis.cf_feat |= ATA_FEAT_PACKET_DMADIR;
288*433d6423SLionel Sambuc 
289*433d6423SLionel Sambuc 		prd[0].vp_addr = ps->tmp_phys;
290*433d6423SLionel Sambuc 		prd[0].vp_size = size;
291*433d6423SLionel Sambuc 		nr_prds++;
292*433d6423SLionel Sambuc 	}
293*433d6423SLionel Sambuc 
294*433d6423SLionel Sambuc 	/* Start the command, and wait for it to complete or fail. */
295*433d6423SLionel Sambuc 	port_set_cmd(ps, cmd, &fis, packet, prd, nr_prds, write);
296*433d6423SLionel Sambuc 
297*433d6423SLionel Sambuc 	return port_exec(ps, cmd, ahci_command_timeout);
298*433d6423SLionel Sambuc }
299*433d6423SLionel Sambuc 
300*433d6423SLionel Sambuc /*===========================================================================*
301*433d6423SLionel Sambuc  *				atapi_test_unit				     *
302*433d6423SLionel Sambuc  *===========================================================================*/
303*433d6423SLionel Sambuc static int atapi_test_unit(struct port_state *ps, int cmd)
304*433d6423SLionel Sambuc {
305*433d6423SLionel Sambuc 	/* Test whether the ATAPI device and medium are ready.
306*433d6423SLionel Sambuc 	 */
307*433d6423SLionel Sambuc 	u8_t packet[ATAPI_PACKET_SIZE];
308*433d6423SLionel Sambuc 
309*433d6423SLionel Sambuc 	memset(packet, 0, sizeof(packet));
310*433d6423SLionel Sambuc 	packet[0] = ATAPI_CMD_TEST_UNIT;
311*433d6423SLionel Sambuc 
312*433d6423SLionel Sambuc 	return atapi_exec(ps, cmd, packet, 0, FALSE);
313*433d6423SLionel Sambuc }
314*433d6423SLionel Sambuc 
315*433d6423SLionel Sambuc /*===========================================================================*
316*433d6423SLionel Sambuc  *				atapi_request_sense			     *
317*433d6423SLionel Sambuc  *===========================================================================*/
318*433d6423SLionel Sambuc static int atapi_request_sense(struct port_state *ps, int cmd, int *sense)
319*433d6423SLionel Sambuc {
320*433d6423SLionel Sambuc 	/* Request error (sense) information from an ATAPI device, and return
321*433d6423SLionel Sambuc 	 * the sense key. The additional sense codes are not used at this time.
322*433d6423SLionel Sambuc 	 */
323*433d6423SLionel Sambuc 	u8_t packet[ATAPI_PACKET_SIZE];
324*433d6423SLionel Sambuc 	int r;
325*433d6423SLionel Sambuc 
326*433d6423SLionel Sambuc 	memset(packet, 0, sizeof(packet));
327*433d6423SLionel Sambuc 	packet[0] = ATAPI_CMD_REQUEST_SENSE;
328*433d6423SLionel Sambuc 	packet[4] = ATAPI_REQUEST_SENSE_LEN;
329*433d6423SLionel Sambuc 
330*433d6423SLionel Sambuc 	r = atapi_exec(ps, cmd, packet, ATAPI_REQUEST_SENSE_LEN, FALSE);
331*433d6423SLionel Sambuc 
332*433d6423SLionel Sambuc 	if (r != OK)
333*433d6423SLionel Sambuc 		return r;
334*433d6423SLionel Sambuc 
335*433d6423SLionel Sambuc 	dprintf(V_REQ, ("%s: ATAPI SENSE: sense %x ASC %x ASCQ %x\n",
336*433d6423SLionel Sambuc 		ahci_portname(ps), ps->tmp_base[2] & 0xF, ps->tmp_base[12],
337*433d6423SLionel Sambuc 		ps->tmp_base[13]));
338*433d6423SLionel Sambuc 
339*433d6423SLionel Sambuc 	*sense = ps->tmp_base[2] & 0xF;
340*433d6423SLionel Sambuc 
341*433d6423SLionel Sambuc 	return OK;
342*433d6423SLionel Sambuc }
343*433d6423SLionel Sambuc 
344*433d6423SLionel Sambuc /*===========================================================================*
345*433d6423SLionel Sambuc  *				atapi_load_eject			     *
346*433d6423SLionel Sambuc  *===========================================================================*/
347*433d6423SLionel Sambuc static int atapi_load_eject(struct port_state *ps, int cmd, int load)
348*433d6423SLionel Sambuc {
349*433d6423SLionel Sambuc 	/* Load or eject a medium in an ATAPI device.
350*433d6423SLionel Sambuc 	 */
351*433d6423SLionel Sambuc 	u8_t packet[ATAPI_PACKET_SIZE];
352*433d6423SLionel Sambuc 
353*433d6423SLionel Sambuc 	memset(packet, 0, sizeof(packet));
354*433d6423SLionel Sambuc 	packet[0] = ATAPI_CMD_START_STOP;
355*433d6423SLionel Sambuc 	packet[4] = load ? ATAPI_START_STOP_LOAD : ATAPI_START_STOP_EJECT;
356*433d6423SLionel Sambuc 
357*433d6423SLionel Sambuc 	return atapi_exec(ps, cmd, packet, 0, FALSE);
358*433d6423SLionel Sambuc }
359*433d6423SLionel Sambuc 
360*433d6423SLionel Sambuc /*===========================================================================*
361*433d6423SLionel Sambuc  *				atapi_read_capacity			     *
362*433d6423SLionel Sambuc  *===========================================================================*/
363*433d6423SLionel Sambuc static int atapi_read_capacity(struct port_state *ps, int cmd)
364*433d6423SLionel Sambuc {
365*433d6423SLionel Sambuc 	/* Retrieve the LBA count and sector size of an ATAPI medium.
366*433d6423SLionel Sambuc 	 */
367*433d6423SLionel Sambuc 	u8_t packet[ATAPI_PACKET_SIZE], *buf;
368*433d6423SLionel Sambuc 	int r;
369*433d6423SLionel Sambuc 
370*433d6423SLionel Sambuc 	memset(packet, 0, sizeof(packet));
371*433d6423SLionel Sambuc 	packet[0] = ATAPI_CMD_READ_CAPACITY;
372*433d6423SLionel Sambuc 
373*433d6423SLionel Sambuc 	r = atapi_exec(ps, cmd, packet, ATAPI_READ_CAPACITY_LEN, FALSE);
374*433d6423SLionel Sambuc 	if (r != OK)
375*433d6423SLionel Sambuc 		return r;
376*433d6423SLionel Sambuc 
377*433d6423SLionel Sambuc 	/* Store the number of LBA blocks and sector size. */
378*433d6423SLionel Sambuc 	buf = ps->tmp_base;
379*433d6423SLionel Sambuc 	ps->lba_count = (u64_t) ((buf[0] << 24) | (buf[1] << 16) |
380*433d6423SLionel Sambuc 		(buf[2] << 8) | buf[3]) + 1;
381*433d6423SLionel Sambuc 	ps->sector_size =
382*433d6423SLionel Sambuc 		(buf[4] << 24) | (buf[5] << 16) | (buf[6] << 8) | buf[7];
383*433d6423SLionel Sambuc 
384*433d6423SLionel Sambuc 	if (ps->sector_size == 0 || (ps->sector_size & 1)) {
385*433d6423SLionel Sambuc 		dprintf(V_ERR, ("%s: invalid medium sector size %u\n",
386*433d6423SLionel Sambuc 			ahci_portname(ps), ps->sector_size));
387*433d6423SLionel Sambuc 
388*433d6423SLionel Sambuc 		return EINVAL;
389*433d6423SLionel Sambuc 	}
390*433d6423SLionel Sambuc 
391*433d6423SLionel Sambuc 	dprintf(V_INFO,
392*433d6423SLionel Sambuc 		("%s: medium detected (%u byte sectors, %llu MB size)\n",
393*433d6423SLionel Sambuc 		ahci_portname(ps), ps->sector_size,
394*433d6423SLionel Sambuc 		ps->lba_count * ps->sector_size / (1024*1024)));
395*433d6423SLionel Sambuc 
396*433d6423SLionel Sambuc 	return OK;
397*433d6423SLionel Sambuc }
398*433d6423SLionel Sambuc 
399*433d6423SLionel Sambuc /*===========================================================================*
400*433d6423SLionel Sambuc  *				atapi_check_medium			     *
401*433d6423SLionel Sambuc  *===========================================================================*/
402*433d6423SLionel Sambuc static int atapi_check_medium(struct port_state *ps, int cmd)
403*433d6423SLionel Sambuc {
404*433d6423SLionel Sambuc 	/* Check whether a medium is present in a removable-media ATAPI device.
405*433d6423SLionel Sambuc 	 * If a new medium is detected, get its total and sector size. Return
406*433d6423SLionel Sambuc 	 * OK only if a usable medium is present, and an error otherwise.
407*433d6423SLionel Sambuc 	 */
408*433d6423SLionel Sambuc 	int sense;
409*433d6423SLionel Sambuc 
410*433d6423SLionel Sambuc 	/* Perform a readiness check. */
411*433d6423SLionel Sambuc 	if (atapi_test_unit(ps, cmd) != OK) {
412*433d6423SLionel Sambuc 		ps->flags &= ~FLAG_HAS_MEDIUM;
413*433d6423SLionel Sambuc 
414*433d6423SLionel Sambuc 		/* If the check failed due to a unit attention condition, retry
415*433d6423SLionel Sambuc 		 * reading the medium capacity. Otherwise, assume that there is
416*433d6423SLionel Sambuc 		 * no medium available.
417*433d6423SLionel Sambuc 		 */
418*433d6423SLionel Sambuc 		if (atapi_request_sense(ps, cmd, &sense) != OK ||
419*433d6423SLionel Sambuc 				sense != ATAPI_SENSE_UNIT_ATT)
420*433d6423SLionel Sambuc 			return ENXIO;
421*433d6423SLionel Sambuc 	}
422*433d6423SLionel Sambuc 
423*433d6423SLionel Sambuc 	/* If a medium is newly detected, try reading its capacity now. */
424*433d6423SLionel Sambuc 	if (!(ps->flags & FLAG_HAS_MEDIUM)) {
425*433d6423SLionel Sambuc 		if (atapi_read_capacity(ps, cmd) != OK)
426*433d6423SLionel Sambuc 			return EIO;
427*433d6423SLionel Sambuc 
428*433d6423SLionel Sambuc 		ps->flags |= FLAG_HAS_MEDIUM;
429*433d6423SLionel Sambuc 	}
430*433d6423SLionel Sambuc 
431*433d6423SLionel Sambuc 	return OK;
432*433d6423SLionel Sambuc }
433*433d6423SLionel Sambuc 
434*433d6423SLionel Sambuc /*===========================================================================*
435*433d6423SLionel Sambuc  *				atapi_id_check				     *
436*433d6423SLionel Sambuc  *===========================================================================*/
437*433d6423SLionel Sambuc static int atapi_id_check(struct port_state *ps, u16_t *buf)
438*433d6423SLionel Sambuc {
439*433d6423SLionel Sambuc 	/* Determine whether we support this ATAPI device based on the
440*433d6423SLionel Sambuc 	 * identification data it returned, and store some of its properties.
441*433d6423SLionel Sambuc 	 */
442*433d6423SLionel Sambuc 
443*433d6423SLionel Sambuc 	/* The device must be an ATAPI device; it must have removable media;
444*433d6423SLionel Sambuc 	 * it must support DMA without DMADIR, or DMADIR for DMA.
445*433d6423SLionel Sambuc 	 */
446*433d6423SLionel Sambuc 	if ((buf[ATA_ID_GCAP] & (ATA_ID_GCAP_ATAPI_MASK |
447*433d6423SLionel Sambuc 		ATA_ID_GCAP_REMOVABLE | ATA_ID_GCAP_INCOMPLETE)) !=
448*433d6423SLionel Sambuc 		(ATA_ID_GCAP_ATAPI | ATA_ID_GCAP_REMOVABLE) ||
449*433d6423SLionel Sambuc 		((buf[ATA_ID_CAP] & ATA_ID_CAP_DMA) != ATA_ID_CAP_DMA &&
450*433d6423SLionel Sambuc 		(buf[ATA_ID_DMADIR] & (ATA_ID_DMADIR_DMADIR |
451*433d6423SLionel Sambuc 		ATA_ID_DMADIR_DMA)) != (ATA_ID_DMADIR_DMADIR |
452*433d6423SLionel Sambuc 		ATA_ID_DMADIR_DMA))) {
453*433d6423SLionel Sambuc 
454*433d6423SLionel Sambuc 		dprintf(V_ERR, ("%s: unsupported ATAPI device\n",
455*433d6423SLionel Sambuc 			ahci_portname(ps)));
456*433d6423SLionel Sambuc 
457*433d6423SLionel Sambuc 		dprintf(V_DEV, ("%s: GCAP %04x CAP %04x DMADIR %04x\n",
458*433d6423SLionel Sambuc 			ahci_portname(ps), buf[ATA_ID_GCAP], buf[ATA_ID_CAP],
459*433d6423SLionel Sambuc 			buf[ATA_ID_DMADIR]));
460*433d6423SLionel Sambuc 
461*433d6423SLionel Sambuc 		return FALSE;
462*433d6423SLionel Sambuc 	}
463*433d6423SLionel Sambuc 
464*433d6423SLionel Sambuc 	/* Remember whether to use the DMADIR flag when appropriate. */
465*433d6423SLionel Sambuc 	if (buf[ATA_ID_DMADIR] & ATA_ID_DMADIR_DMADIR)
466*433d6423SLionel Sambuc 		ps->flags |= FLAG_USE_DMADIR;
467*433d6423SLionel Sambuc 
468*433d6423SLionel Sambuc 	/* ATAPI CD-ROM devices are considered read-only. */
469*433d6423SLionel Sambuc 	if (((buf[ATA_ID_GCAP] & ATA_ID_GCAP_TYPE_MASK) >>
470*433d6423SLionel Sambuc 		ATA_ID_GCAP_TYPE_SHIFT) == ATAPI_TYPE_CDROM)
471*433d6423SLionel Sambuc 		ps->flags |= FLAG_READONLY;
472*433d6423SLionel Sambuc 
473*433d6423SLionel Sambuc 	if ((buf[ATA_ID_SUP1] & ATA_ID_SUP1_VALID_MASK) == ATA_ID_SUP1_VALID &&
474*433d6423SLionel Sambuc 		!(ps->flags & FLAG_READONLY)) {
475*433d6423SLionel Sambuc 		/* Save write cache related capabilities of the device. It is
476*433d6423SLionel Sambuc 		 * possible, although unlikely, that a device has support for
477*433d6423SLionel Sambuc 		 * either of these but not both.
478*433d6423SLionel Sambuc 		 */
479*433d6423SLionel Sambuc 		if (buf[ATA_ID_SUP0] & ATA_ID_SUP0_WCACHE)
480*433d6423SLionel Sambuc 			ps->flags |= FLAG_HAS_WCACHE;
481*433d6423SLionel Sambuc 
482*433d6423SLionel Sambuc 		if (buf[ATA_ID_SUP1] & ATA_ID_SUP1_FLUSH)
483*433d6423SLionel Sambuc 			ps->flags |= FLAG_HAS_FLUSH;
484*433d6423SLionel Sambuc 	}
485*433d6423SLionel Sambuc 
486*433d6423SLionel Sambuc 	return TRUE;
487*433d6423SLionel Sambuc }
488*433d6423SLionel Sambuc 
489*433d6423SLionel Sambuc /*===========================================================================*
490*433d6423SLionel Sambuc  *				atapi_transfer				     *
491*433d6423SLionel Sambuc  *===========================================================================*/
492*433d6423SLionel Sambuc static int atapi_transfer(struct port_state *ps, int cmd, u64_t start_lba,
493*433d6423SLionel Sambuc 	unsigned int count, int write, prd_t *prdt, int nr_prds)
494*433d6423SLionel Sambuc {
495*433d6423SLionel Sambuc 	/* Perform data transfer from or to an ATAPI device.
496*433d6423SLionel Sambuc 	 */
497*433d6423SLionel Sambuc 	cmd_fis_t fis;
498*433d6423SLionel Sambuc 	u8_t packet[ATAPI_PACKET_SIZE];
499*433d6423SLionel Sambuc 
500*433d6423SLionel Sambuc 	/* Fill in a Register Host to Device FIS. */
501*433d6423SLionel Sambuc 	memset(&fis, 0, sizeof(fis));
502*433d6423SLionel Sambuc 	fis.cf_cmd = ATA_CMD_PACKET;
503*433d6423SLionel Sambuc 	fis.cf_feat = ATA_FEAT_PACKET_DMA;
504*433d6423SLionel Sambuc 	if (!write && (ps->flags & FLAG_USE_DMADIR))
505*433d6423SLionel Sambuc 		fis.cf_feat |= ATA_FEAT_PACKET_DMADIR;
506*433d6423SLionel Sambuc 
507*433d6423SLionel Sambuc 	/* Fill in a packet. */
508*433d6423SLionel Sambuc 	memset(packet, 0, sizeof(packet));
509*433d6423SLionel Sambuc 	packet[0] = write ? ATAPI_CMD_WRITE : ATAPI_CMD_READ;
510*433d6423SLionel Sambuc 	packet[2] = (start_lba >> 24) & 0xFF;
511*433d6423SLionel Sambuc 	packet[3] = (start_lba >> 16) & 0xFF;
512*433d6423SLionel Sambuc 	packet[4] = (start_lba >>  8) & 0xFF;
513*433d6423SLionel Sambuc 	packet[5] = start_lba & 0xFF;
514*433d6423SLionel Sambuc 	packet[6] = (count >> 24) & 0xFF;
515*433d6423SLionel Sambuc 	packet[7] = (count >> 16) & 0xFF;
516*433d6423SLionel Sambuc 	packet[8] = (count >>  8) & 0xFF;
517*433d6423SLionel Sambuc 	packet[9] = count & 0xFF;
518*433d6423SLionel Sambuc 
519*433d6423SLionel Sambuc 	/* Start the command, and wait for it to complete or fail. */
520*433d6423SLionel Sambuc 	port_set_cmd(ps, cmd, &fis, packet, prdt, nr_prds, write);
521*433d6423SLionel Sambuc 
522*433d6423SLionel Sambuc 	return port_exec(ps, cmd, ahci_transfer_timeout);
523*433d6423SLionel Sambuc }
524*433d6423SLionel Sambuc 
525*433d6423SLionel Sambuc /*===========================================================================*
526*433d6423SLionel Sambuc  *				ata_id_check				     *
527*433d6423SLionel Sambuc  *===========================================================================*/
528*433d6423SLionel Sambuc static int ata_id_check(struct port_state *ps, u16_t *buf)
529*433d6423SLionel Sambuc {
530*433d6423SLionel Sambuc 	/* Determine whether we support this ATA device based on the
531*433d6423SLionel Sambuc 	 * identification data it returned, and store some of its properties.
532*433d6423SLionel Sambuc 	 */
533*433d6423SLionel Sambuc 
534*433d6423SLionel Sambuc 	/* This must be an ATA device; it must not have removable media;
535*433d6423SLionel Sambuc 	 * it must support LBA and DMA; it must support the FLUSH CACHE
536*433d6423SLionel Sambuc 	 * command; it must support 48-bit addressing.
537*433d6423SLionel Sambuc 	 */
538*433d6423SLionel Sambuc 	if ((buf[ATA_ID_GCAP] & (ATA_ID_GCAP_ATA_MASK | ATA_ID_GCAP_REMOVABLE |
539*433d6423SLionel Sambuc 		ATA_ID_GCAP_INCOMPLETE)) != ATA_ID_GCAP_ATA ||
540*433d6423SLionel Sambuc 		(buf[ATA_ID_CAP] & (ATA_ID_CAP_LBA | ATA_ID_CAP_DMA)) !=
541*433d6423SLionel Sambuc 		(ATA_ID_CAP_LBA | ATA_ID_CAP_DMA) ||
542*433d6423SLionel Sambuc 		(buf[ATA_ID_SUP1] & (ATA_ID_SUP1_VALID_MASK |
543*433d6423SLionel Sambuc 		ATA_ID_SUP1_FLUSH | ATA_ID_SUP1_LBA48)) !=
544*433d6423SLionel Sambuc 		(ATA_ID_SUP1_VALID | ATA_ID_SUP1_FLUSH | ATA_ID_SUP1_LBA48)) {
545*433d6423SLionel Sambuc 
546*433d6423SLionel Sambuc 		dprintf(V_ERR, ("%s: unsupported ATA device\n",
547*433d6423SLionel Sambuc 			ahci_portname(ps)));
548*433d6423SLionel Sambuc 
549*433d6423SLionel Sambuc 		dprintf(V_DEV, ("%s: GCAP %04x CAP %04x SUP1 %04x\n",
550*433d6423SLionel Sambuc 			ahci_portname(ps), buf[ATA_ID_GCAP], buf[ATA_ID_CAP],
551*433d6423SLionel Sambuc 			buf[ATA_ID_SUP1]));
552*433d6423SLionel Sambuc 
553*433d6423SLionel Sambuc 		return FALSE;
554*433d6423SLionel Sambuc 	}
555*433d6423SLionel Sambuc 
556*433d6423SLionel Sambuc 	/* Get number of LBA blocks, and sector size. */
557*433d6423SLionel Sambuc 	ps->lba_count = ((u64_t) buf[ATA_ID_LBA3] << 48) |
558*433d6423SLionel Sambuc 			((u64_t) buf[ATA_ID_LBA2] << 32) |
559*433d6423SLionel Sambuc 			((u64_t) buf[ATA_ID_LBA1] << 16) |
560*433d6423SLionel Sambuc 			 (u64_t) buf[ATA_ID_LBA0];
561*433d6423SLionel Sambuc 
562*433d6423SLionel Sambuc 	/* Determine the queue depth of the device. */
563*433d6423SLionel Sambuc 	if (hba_state.has_ncq &&
564*433d6423SLionel Sambuc 			(buf[ATA_ID_SATA_CAP] & ATA_ID_SATA_CAP_NCQ)) {
565*433d6423SLionel Sambuc 		ps->flags |= FLAG_HAS_NCQ;
566*433d6423SLionel Sambuc 		ps->queue_depth =
567*433d6423SLionel Sambuc 			(buf[ATA_ID_QDEPTH] & ATA_ID_QDEPTH_MASK) + 1;
568*433d6423SLionel Sambuc 		if (ps->queue_depth > hba_state.nr_cmds)
569*433d6423SLionel Sambuc 			ps->queue_depth = hba_state.nr_cmds;
570*433d6423SLionel Sambuc 	}
571*433d6423SLionel Sambuc 
572*433d6423SLionel Sambuc 	/* For now, we only support long logical sectors. Long physical sector
573*433d6423SLionel Sambuc 	 * support may be added later. Note that the given value is in words.
574*433d6423SLionel Sambuc 	 */
575*433d6423SLionel Sambuc 	if ((buf[ATA_ID_PLSS] & (ATA_ID_PLSS_VALID_MASK | ATA_ID_PLSS_LLS)) ==
576*433d6423SLionel Sambuc 		(ATA_ID_PLSS_VALID | ATA_ID_PLSS_LLS))
577*433d6423SLionel Sambuc 		ps->sector_size =
578*433d6423SLionel Sambuc 			((buf[ATA_ID_LSS1] << 16) | buf[ATA_ID_LSS0]) << 1;
579*433d6423SLionel Sambuc 	else
580*433d6423SLionel Sambuc 		ps->sector_size = ATA_SECTOR_SIZE;
581*433d6423SLionel Sambuc 
582*433d6423SLionel Sambuc 	if (ps->sector_size < ATA_SECTOR_SIZE) {
583*433d6423SLionel Sambuc 		dprintf(V_ERR, ("%s: invalid sector size %u\n",
584*433d6423SLionel Sambuc 			ahci_portname(ps), ps->sector_size));
585*433d6423SLionel Sambuc 
586*433d6423SLionel Sambuc 		return FALSE;
587*433d6423SLionel Sambuc 	}
588*433d6423SLionel Sambuc 
589*433d6423SLionel Sambuc 	ps->flags |= FLAG_HAS_MEDIUM | FLAG_HAS_FLUSH;
590*433d6423SLionel Sambuc 
591*433d6423SLionel Sambuc 	/* FLUSH CACHE is mandatory for ATA devices; write caches are not. */
592*433d6423SLionel Sambuc 	if (buf[ATA_ID_SUP0] & ATA_ID_SUP0_WCACHE)
593*433d6423SLionel Sambuc 		ps->flags |= FLAG_HAS_WCACHE;
594*433d6423SLionel Sambuc 
595*433d6423SLionel Sambuc 	/* Check Force Unit Access capability of the device. */
596*433d6423SLionel Sambuc 	if ((buf[ATA_ID_ENA2] & (ATA_ID_ENA2_VALID_MASK | ATA_ID_ENA2_FUA)) ==
597*433d6423SLionel Sambuc 		(ATA_ID_ENA2_VALID | ATA_ID_ENA2_FUA))
598*433d6423SLionel Sambuc 		ps->flags |= FLAG_HAS_FUA;
599*433d6423SLionel Sambuc 
600*433d6423SLionel Sambuc 	return TRUE;
601*433d6423SLionel Sambuc }
602*433d6423SLionel Sambuc 
603*433d6423SLionel Sambuc /*===========================================================================*
604*433d6423SLionel Sambuc  *				ata_transfer				     *
605*433d6423SLionel Sambuc  *===========================================================================*/
606*433d6423SLionel Sambuc static int ata_transfer(struct port_state *ps, int cmd, u64_t start_lba,
607*433d6423SLionel Sambuc 	unsigned int count, int write, int force, prd_t *prdt, int nr_prds)
608*433d6423SLionel Sambuc {
609*433d6423SLionel Sambuc 	/* Perform data transfer from or to an ATA device.
610*433d6423SLionel Sambuc 	 */
611*433d6423SLionel Sambuc 	cmd_fis_t fis;
612*433d6423SLionel Sambuc 
613*433d6423SLionel Sambuc 	assert(count <= ATA_MAX_SECTORS);
614*433d6423SLionel Sambuc 
615*433d6423SLionel Sambuc 	/* Special case for sector counts: 65536 is specified as 0. */
616*433d6423SLionel Sambuc 	if (count == ATA_MAX_SECTORS)
617*433d6423SLionel Sambuc 		count = 0;
618*433d6423SLionel Sambuc 
619*433d6423SLionel Sambuc 	memset(&fis, 0, sizeof(fis));
620*433d6423SLionel Sambuc 	fis.cf_dev = ATA_DEV_LBA;
621*433d6423SLionel Sambuc 	if (ps->flags & FLAG_HAS_NCQ) {
622*433d6423SLionel Sambuc 		if (write) {
623*433d6423SLionel Sambuc 			if (force && (ps->flags & FLAG_HAS_FUA))
624*433d6423SLionel Sambuc 				fis.cf_dev |= ATA_DEV_FUA;
625*433d6423SLionel Sambuc 
626*433d6423SLionel Sambuc 			fis.cf_cmd = ATA_CMD_WRITE_FPDMA_QUEUED;
627*433d6423SLionel Sambuc 		} else {
628*433d6423SLionel Sambuc 			fis.cf_cmd = ATA_CMD_READ_FPDMA_QUEUED;
629*433d6423SLionel Sambuc 		}
630*433d6423SLionel Sambuc 	}
631*433d6423SLionel Sambuc 	else {
632*433d6423SLionel Sambuc 		if (write) {
633*433d6423SLionel Sambuc 			if (force && (ps->flags & FLAG_HAS_FUA))
634*433d6423SLionel Sambuc 				fis.cf_cmd = ATA_CMD_WRITE_DMA_FUA_EXT;
635*433d6423SLionel Sambuc 			else
636*433d6423SLionel Sambuc 				fis.cf_cmd = ATA_CMD_WRITE_DMA_EXT;
637*433d6423SLionel Sambuc 		}
638*433d6423SLionel Sambuc 		else {
639*433d6423SLionel Sambuc 			fis.cf_cmd = ATA_CMD_READ_DMA_EXT;
640*433d6423SLionel Sambuc 		}
641*433d6423SLionel Sambuc 	}
642*433d6423SLionel Sambuc 	fis.cf_lba = start_lba & 0x00FFFFFFUL;
643*433d6423SLionel Sambuc 	fis.cf_lba_exp = (start_lba >> 24) & 0x00FFFFFFUL;
644*433d6423SLionel Sambuc 	fis.cf_sec = count & 0xFF;
645*433d6423SLionel Sambuc 	fis.cf_sec_exp = (count >> 8) & 0xFF;
646*433d6423SLionel Sambuc 
647*433d6423SLionel Sambuc 	/* Start the command, and wait for it to complete or fail. */
648*433d6423SLionel Sambuc 	port_set_cmd(ps, cmd, &fis, NULL /*packet*/, prdt, nr_prds, write);
649*433d6423SLionel Sambuc 
650*433d6423SLionel Sambuc 	return port_exec(ps, cmd, ahci_transfer_timeout);
651*433d6423SLionel Sambuc }
652*433d6423SLionel Sambuc 
653*433d6423SLionel Sambuc /*===========================================================================*
654*433d6423SLionel Sambuc  *				gen_identify				     *
655*433d6423SLionel Sambuc  *===========================================================================*/
656*433d6423SLionel Sambuc static int gen_identify(struct port_state *ps, int blocking)
657*433d6423SLionel Sambuc {
658*433d6423SLionel Sambuc 	/* Identify an ATA or ATAPI device. If the blocking flag is set, block
659*433d6423SLionel Sambuc 	 * until the command has completed; otherwise return immediately.
660*433d6423SLionel Sambuc 	 */
661*433d6423SLionel Sambuc 	cmd_fis_t fis;
662*433d6423SLionel Sambuc 	prd_t prd;
663*433d6423SLionel Sambuc 
664*433d6423SLionel Sambuc 	/* Set up a command, and a single PRD for the result. */
665*433d6423SLionel Sambuc 	memset(&fis, 0, sizeof(fis));
666*433d6423SLionel Sambuc 
667*433d6423SLionel Sambuc 	if (ps->flags & FLAG_ATAPI)
668*433d6423SLionel Sambuc 		fis.cf_cmd = ATA_CMD_IDENTIFY_PACKET;
669*433d6423SLionel Sambuc 	else
670*433d6423SLionel Sambuc 		fis.cf_cmd = ATA_CMD_IDENTIFY;
671*433d6423SLionel Sambuc 
672*433d6423SLionel Sambuc 	prd.vp_addr = ps->tmp_phys;
673*433d6423SLionel Sambuc 	prd.vp_size = ATA_ID_SIZE;
674*433d6423SLionel Sambuc 
675*433d6423SLionel Sambuc 	/* Start the command, and possibly wait for the result. */
676*433d6423SLionel Sambuc 	port_set_cmd(ps, 0, &fis, NULL /*packet*/, &prd, 1, FALSE /*write*/);
677*433d6423SLionel Sambuc 
678*433d6423SLionel Sambuc 	if (blocking)
679*433d6423SLionel Sambuc 		return port_exec(ps, 0, ahci_command_timeout);
680*433d6423SLionel Sambuc 
681*433d6423SLionel Sambuc 	port_issue(ps, 0, ahci_command_timeout);
682*433d6423SLionel Sambuc 
683*433d6423SLionel Sambuc 	return OK;
684*433d6423SLionel Sambuc }
685*433d6423SLionel Sambuc 
686*433d6423SLionel Sambuc /*===========================================================================*
687*433d6423SLionel Sambuc  *				gen_flush_wcache			     *
688*433d6423SLionel Sambuc  *===========================================================================*/
689*433d6423SLionel Sambuc static int gen_flush_wcache(struct port_state *ps)
690*433d6423SLionel Sambuc {
691*433d6423SLionel Sambuc 	/* Flush the device's write cache.
692*433d6423SLionel Sambuc 	 */
693*433d6423SLionel Sambuc 	cmd_fis_t fis;
694*433d6423SLionel Sambuc 
695*433d6423SLionel Sambuc 	/* The FLUSH CACHE command may not be supported by all (writable ATAPI)
696*433d6423SLionel Sambuc 	 * devices.
697*433d6423SLionel Sambuc 	 */
698*433d6423SLionel Sambuc 	if (!(ps->flags & FLAG_HAS_FLUSH))
699*433d6423SLionel Sambuc 		return EINVAL;
700*433d6423SLionel Sambuc 
701*433d6423SLionel Sambuc 	/* Use the FLUSH CACHE command for both ATA and ATAPI. We are not
702*433d6423SLionel Sambuc 	 * interested in the disk location of a failure, so there is no reason
703*433d6423SLionel Sambuc 	 * to use the ATA-only FLUSH CACHE EXT command. Either way, the command
704*433d6423SLionel Sambuc 	 * may indeed fail due to a disk error, in which case it should be
705*433d6423SLionel Sambuc 	 * repeated. For now, we shift this responsibility onto the caller.
706*433d6423SLionel Sambuc 	 */
707*433d6423SLionel Sambuc 	memset(&fis, 0, sizeof(fis));
708*433d6423SLionel Sambuc 	fis.cf_cmd = ATA_CMD_FLUSH_CACHE;
709*433d6423SLionel Sambuc 
710*433d6423SLionel Sambuc 	/* Start the command, and wait for it to complete or fail.
711*433d6423SLionel Sambuc 	 * The flush command may take longer than regular I/O commands.
712*433d6423SLionel Sambuc 	 */
713*433d6423SLionel Sambuc 	port_set_cmd(ps, 0, &fis, NULL /*packet*/, NULL /*prdt*/, 0,
714*433d6423SLionel Sambuc 		FALSE /*write*/);
715*433d6423SLionel Sambuc 
716*433d6423SLionel Sambuc 	return port_exec(ps, 0, ahci_flush_timeout);
717*433d6423SLionel Sambuc }
718*433d6423SLionel Sambuc 
719*433d6423SLionel Sambuc /*===========================================================================*
720*433d6423SLionel Sambuc  *				gen_get_wcache				     *
721*433d6423SLionel Sambuc  *===========================================================================*/
722*433d6423SLionel Sambuc static int gen_get_wcache(struct port_state *ps, int *val)
723*433d6423SLionel Sambuc {
724*433d6423SLionel Sambuc 	/* Retrieve the status of the device's write cache.
725*433d6423SLionel Sambuc 	 */
726*433d6423SLionel Sambuc 	int r;
727*433d6423SLionel Sambuc 
728*433d6423SLionel Sambuc 	/* Write caches are not mandatory. */
729*433d6423SLionel Sambuc 	if (!(ps->flags & FLAG_HAS_WCACHE))
730*433d6423SLionel Sambuc 		return EINVAL;
731*433d6423SLionel Sambuc 
732*433d6423SLionel Sambuc 	/* Retrieve information about the device. */
733*433d6423SLionel Sambuc 	if ((r = gen_identify(ps, TRUE /*blocking*/)) != OK)
734*433d6423SLionel Sambuc 		return r;
735*433d6423SLionel Sambuc 
736*433d6423SLionel Sambuc 	/* Return the current setting. */
737*433d6423SLionel Sambuc 	*val = !!(((u16_t *) ps->tmp_base)[ATA_ID_ENA0] & ATA_ID_ENA0_WCACHE);
738*433d6423SLionel Sambuc 
739*433d6423SLionel Sambuc 	return OK;
740*433d6423SLionel Sambuc }
741*433d6423SLionel Sambuc 
742*433d6423SLionel Sambuc /*===========================================================================*
743*433d6423SLionel Sambuc  *				gen_set_wcache				     *
744*433d6423SLionel Sambuc  *===========================================================================*/
745*433d6423SLionel Sambuc static int gen_set_wcache(struct port_state *ps, int enable)
746*433d6423SLionel Sambuc {
747*433d6423SLionel Sambuc 	/* Enable or disable the device's write cache.
748*433d6423SLionel Sambuc 	 */
749*433d6423SLionel Sambuc 	cmd_fis_t fis;
750*433d6423SLionel Sambuc 	clock_t timeout;
751*433d6423SLionel Sambuc 
752*433d6423SLionel Sambuc 	/* Write caches are not mandatory. */
753*433d6423SLionel Sambuc 	if (!(ps->flags & FLAG_HAS_WCACHE))
754*433d6423SLionel Sambuc 		return EINVAL;
755*433d6423SLionel Sambuc 
756*433d6423SLionel Sambuc 	/* Disabling the write cache causes a (blocking) cache flush. Cache
757*433d6423SLionel Sambuc 	 * flushes may take much longer than regular commands.
758*433d6423SLionel Sambuc 	 */
759*433d6423SLionel Sambuc 	timeout = enable ? ahci_command_timeout : ahci_flush_timeout;
760*433d6423SLionel Sambuc 
761*433d6423SLionel Sambuc 	/* Set up a command. */
762*433d6423SLionel Sambuc 	memset(&fis, 0, sizeof(fis));
763*433d6423SLionel Sambuc 	fis.cf_cmd = ATA_CMD_SET_FEATURES;
764*433d6423SLionel Sambuc 	fis.cf_feat = enable ? ATA_SF_EN_WCACHE : ATA_SF_DI_WCACHE;
765*433d6423SLionel Sambuc 
766*433d6423SLionel Sambuc 	/* Start the command, and wait for it to complete or fail. */
767*433d6423SLionel Sambuc 	port_set_cmd(ps, 0, &fis, NULL /*packet*/, NULL /*prdt*/, 0,
768*433d6423SLionel Sambuc 		FALSE /*write*/);
769*433d6423SLionel Sambuc 
770*433d6423SLionel Sambuc 	return port_exec(ps, 0, timeout);
771*433d6423SLionel Sambuc }
772*433d6423SLionel Sambuc 
773*433d6423SLionel Sambuc /*===========================================================================*
774*433d6423SLionel Sambuc  *				ct_set_fis				     *
775*433d6423SLionel Sambuc  *===========================================================================*/
776*433d6423SLionel Sambuc static vir_bytes ct_set_fis(u8_t *ct, cmd_fis_t *fis, unsigned int tag)
777*433d6423SLionel Sambuc {
778*433d6423SLionel Sambuc 	/* Fill in the Frame Information Structure part of a command table,
779*433d6423SLionel Sambuc 	 * and return the resulting FIS size (in bytes). We only support the
780*433d6423SLionel Sambuc 	 * command Register - Host to Device FIS type.
781*433d6423SLionel Sambuc 	 */
782*433d6423SLionel Sambuc 
783*433d6423SLionel Sambuc 	memset(ct, 0, ATA_H2D_SIZE);
784*433d6423SLionel Sambuc 	ct[ATA_FIS_TYPE] = ATA_FIS_TYPE_H2D;
785*433d6423SLionel Sambuc 	ct[ATA_H2D_FLAGS] = ATA_H2D_FLAGS_C;
786*433d6423SLionel Sambuc 	ct[ATA_H2D_CMD] = fis->cf_cmd;
787*433d6423SLionel Sambuc 	ct[ATA_H2D_LBA_LOW] = fis->cf_lba & 0xFF;
788*433d6423SLionel Sambuc 	ct[ATA_H2D_LBA_MID] = (fis->cf_lba >> 8) & 0xFF;
789*433d6423SLionel Sambuc 	ct[ATA_H2D_LBA_HIGH] = (fis->cf_lba >> 16) & 0xFF;
790*433d6423SLionel Sambuc 	ct[ATA_H2D_DEV] = fis->cf_dev;
791*433d6423SLionel Sambuc 	ct[ATA_H2D_LBA_LOW_EXP] = fis->cf_lba_exp & 0xFF;
792*433d6423SLionel Sambuc 	ct[ATA_H2D_LBA_MID_EXP] = (fis->cf_lba_exp >> 8) & 0xFF;
793*433d6423SLionel Sambuc 	ct[ATA_H2D_LBA_HIGH_EXP] = (fis->cf_lba_exp >> 16) & 0xFF;
794*433d6423SLionel Sambuc 	ct[ATA_H2D_CTL] = fis->cf_ctl;
795*433d6423SLionel Sambuc 
796*433d6423SLionel Sambuc 	if (ATA_IS_FPDMA_CMD(fis->cf_cmd)) {
797*433d6423SLionel Sambuc 		ct[ATA_H2D_FEAT] = fis->cf_sec;
798*433d6423SLionel Sambuc 		ct[ATA_H2D_FEAT_EXP] = fis->cf_sec_exp;
799*433d6423SLionel Sambuc 		ct[ATA_H2D_SEC] = tag << ATA_SEC_TAG_SHIFT;
800*433d6423SLionel Sambuc 		ct[ATA_H2D_SEC_EXP] = 0;
801*433d6423SLionel Sambuc 	} else {
802*433d6423SLionel Sambuc 		ct[ATA_H2D_FEAT] = fis->cf_feat;
803*433d6423SLionel Sambuc 		ct[ATA_H2D_FEAT_EXP] = fis->cf_feat_exp;
804*433d6423SLionel Sambuc 		ct[ATA_H2D_SEC] = fis->cf_sec;
805*433d6423SLionel Sambuc 		ct[ATA_H2D_SEC_EXP] = fis->cf_sec_exp;
806*433d6423SLionel Sambuc 	}
807*433d6423SLionel Sambuc 
808*433d6423SLionel Sambuc 	return ATA_H2D_SIZE;
809*433d6423SLionel Sambuc }
810*433d6423SLionel Sambuc 
811*433d6423SLionel Sambuc /*===========================================================================*
812*433d6423SLionel Sambuc  *				ct_set_packet				     *
813*433d6423SLionel Sambuc  *===========================================================================*/
814*433d6423SLionel Sambuc static void ct_set_packet(u8_t *ct, u8_t packet[ATAPI_PACKET_SIZE])
815*433d6423SLionel Sambuc {
816*433d6423SLionel Sambuc 	/* Fill in the packet part of a command table.
817*433d6423SLionel Sambuc 	 */
818*433d6423SLionel Sambuc 
819*433d6423SLionel Sambuc 	memcpy(&ct[AHCI_CT_PACKET_OFF], packet, ATAPI_PACKET_SIZE);
820*433d6423SLionel Sambuc }
821*433d6423SLionel Sambuc 
822*433d6423SLionel Sambuc /*===========================================================================*
823*433d6423SLionel Sambuc  *				ct_set_prdt				     *
824*433d6423SLionel Sambuc  *===========================================================================*/
825*433d6423SLionel Sambuc static void ct_set_prdt(u8_t *ct, prd_t *prdt, int nr_prds)
826*433d6423SLionel Sambuc {
827*433d6423SLionel Sambuc 	/* Fill in the PRDT part of a command table.
828*433d6423SLionel Sambuc 	 */
829*433d6423SLionel Sambuc 	u32_t *p;
830*433d6423SLionel Sambuc 	int i;
831*433d6423SLionel Sambuc 
832*433d6423SLionel Sambuc 	p = (u32_t *) &ct[AHCI_CT_PRDT_OFF];
833*433d6423SLionel Sambuc 
834*433d6423SLionel Sambuc 	for (i = 0; i < nr_prds; i++, prdt++) {
835*433d6423SLionel Sambuc 		*p++ = prdt->vp_addr;
836*433d6423SLionel Sambuc 		*p++ = 0;
837*433d6423SLionel Sambuc 		*p++ = 0;
838*433d6423SLionel Sambuc 		*p++ = prdt->vp_size - 1;
839*433d6423SLionel Sambuc 	}
840*433d6423SLionel Sambuc }
841*433d6423SLionel Sambuc 
842*433d6423SLionel Sambuc /*===========================================================================*
843*433d6423SLionel Sambuc  *				port_set_cmd				     *
844*433d6423SLionel Sambuc  *===========================================================================*/
845*433d6423SLionel Sambuc static void port_set_cmd(struct port_state *ps, int cmd, cmd_fis_t *fis,
846*433d6423SLionel Sambuc 	u8_t packet[ATAPI_PACKET_SIZE], prd_t *prdt, int nr_prds, int write)
847*433d6423SLionel Sambuc {
848*433d6423SLionel Sambuc 	/* Prepare the given command for execution, by constructing a command
849*433d6423SLionel Sambuc 	 * table and setting up a command list entry pointing to the table.
850*433d6423SLionel Sambuc 	 */
851*433d6423SLionel Sambuc 	u8_t *ct;
852*433d6423SLionel Sambuc 	u32_t *cl;
853*433d6423SLionel Sambuc 	vir_bytes size;
854*433d6423SLionel Sambuc 
855*433d6423SLionel Sambuc 	/* Set a port-specific flag that tells us if the command being
856*433d6423SLionel Sambuc 	 * processed is a NCQ command or not.
857*433d6423SLionel Sambuc 	 */
858*433d6423SLionel Sambuc 	if (ATA_IS_FPDMA_CMD(fis->cf_cmd)) {
859*433d6423SLionel Sambuc 		ps->flags |= FLAG_NCQ_MODE;
860*433d6423SLionel Sambuc 	} else {
861*433d6423SLionel Sambuc 		assert(!ps->pend_mask);
862*433d6423SLionel Sambuc 		ps->flags &= ~FLAG_NCQ_MODE;
863*433d6423SLionel Sambuc 	}
864*433d6423SLionel Sambuc 
865*433d6423SLionel Sambuc 	/* Construct a command table, consisting of a command FIS, optionally
866*433d6423SLionel Sambuc 	 * a packet, and optionally a number of PRDs (making up the actual PRD
867*433d6423SLionel Sambuc 	 * table).
868*433d6423SLionel Sambuc 	 */
869*433d6423SLionel Sambuc 	ct = ps->ct_base[cmd];
870*433d6423SLionel Sambuc 
871*433d6423SLionel Sambuc 	assert(ct != NULL);
872*433d6423SLionel Sambuc 	assert(nr_prds <= NR_PRDS);
873*433d6423SLionel Sambuc 
874*433d6423SLionel Sambuc 	size = ct_set_fis(ct, fis, cmd);
875*433d6423SLionel Sambuc 
876*433d6423SLionel Sambuc 	if (packet != NULL)
877*433d6423SLionel Sambuc 		ct_set_packet(ct, packet);
878*433d6423SLionel Sambuc 
879*433d6423SLionel Sambuc 	ct_set_prdt(ct, prdt, nr_prds);
880*433d6423SLionel Sambuc 
881*433d6423SLionel Sambuc 	/* Construct a command list entry, pointing to the command's table.
882*433d6423SLionel Sambuc 	 * Current assumptions: callers always provide a Register - Host to
883*433d6423SLionel Sambuc 	 * Device type FIS, and all non-NCQ commands are prefetchable.
884*433d6423SLionel Sambuc 	 */
885*433d6423SLionel Sambuc 	cl = &ps->cl_base[cmd * AHCI_CL_ENTRY_DWORDS];
886*433d6423SLionel Sambuc 
887*433d6423SLionel Sambuc 	memset(cl, 0, AHCI_CL_ENTRY_SIZE);
888*433d6423SLionel Sambuc 	cl[0] = (nr_prds << AHCI_CL_PRDTL_SHIFT) |
889*433d6423SLionel Sambuc 		((!ATA_IS_FPDMA_CMD(fis->cf_cmd) &&
890*433d6423SLionel Sambuc 		(nr_prds > 0 || packet != NULL)) ? AHCI_CL_PREFETCHABLE : 0) |
891*433d6423SLionel Sambuc 		(write ? AHCI_CL_WRITE : 0) |
892*433d6423SLionel Sambuc 		((packet != NULL) ? AHCI_CL_ATAPI : 0) |
893*433d6423SLionel Sambuc 		((size / sizeof(u32_t)) << AHCI_CL_CFL_SHIFT);
894*433d6423SLionel Sambuc 	cl[2] = ps->ct_phys[cmd];
895*433d6423SLionel Sambuc }
896*433d6423SLionel Sambuc 
897*433d6423SLionel Sambuc /*===========================================================================*
898*433d6423SLionel Sambuc  *				port_finish_cmd				     *
899*433d6423SLionel Sambuc  *===========================================================================*/
900*433d6423SLionel Sambuc static void port_finish_cmd(struct port_state *ps, int cmd, int result)
901*433d6423SLionel Sambuc {
902*433d6423SLionel Sambuc 	/* Finish a command that has either succeeded or failed.
903*433d6423SLionel Sambuc 	 */
904*433d6423SLionel Sambuc 
905*433d6423SLionel Sambuc 	assert(cmd < ps->queue_depth);
906*433d6423SLionel Sambuc 
907*433d6423SLionel Sambuc 	dprintf(V_REQ, ("%s: command %d %s\n", ahci_portname(ps),
908*433d6423SLionel Sambuc 		cmd, (result == RESULT_SUCCESS) ? "succeeded" : "failed"));
909*433d6423SLionel Sambuc 
910*433d6423SLionel Sambuc 	/* Update the command result, and clear it from the pending list. */
911*433d6423SLionel Sambuc 	ps->cmd_info[cmd].result = result;
912*433d6423SLionel Sambuc 
913*433d6423SLionel Sambuc 	assert(ps->pend_mask & (1 << cmd));
914*433d6423SLionel Sambuc 	ps->pend_mask &= ~(1 << cmd);
915*433d6423SLionel Sambuc 
916*433d6423SLionel Sambuc 	/* Wake up the thread, unless it is the main thread. This can happen
917*433d6423SLionel Sambuc 	 * during initialization, as the gen_identify function is called by the
918*433d6423SLionel Sambuc 	 * main thread itself.
919*433d6423SLionel Sambuc 	 */
920*433d6423SLionel Sambuc 	if (ps->state != STATE_WAIT_ID)
921*433d6423SLionel Sambuc 		blockdriver_mt_wakeup(ps->cmd_info[cmd].tid);
922*433d6423SLionel Sambuc }
923*433d6423SLionel Sambuc 
924*433d6423SLionel Sambuc /*===========================================================================*
925*433d6423SLionel Sambuc  *				port_fail_cmds				     *
926*433d6423SLionel Sambuc  *===========================================================================*/
927*433d6423SLionel Sambuc static void port_fail_cmds(struct port_state *ps)
928*433d6423SLionel Sambuc {
929*433d6423SLionel Sambuc 	/* Fail all ongoing commands for a device.
930*433d6423SLionel Sambuc 	 */
931*433d6423SLionel Sambuc 	int i;
932*433d6423SLionel Sambuc 
933*433d6423SLionel Sambuc 	for (i = 0; ps->pend_mask != 0 && i < ps->queue_depth; i++)
934*433d6423SLionel Sambuc 		if (ps->pend_mask & (1 << i))
935*433d6423SLionel Sambuc 			port_finish_cmd(ps, i, RESULT_FAILURE);
936*433d6423SLionel Sambuc }
937*433d6423SLionel Sambuc 
938*433d6423SLionel Sambuc /*===========================================================================*
939*433d6423SLionel Sambuc  *				port_check_cmds				     *
940*433d6423SLionel Sambuc  *===========================================================================*/
941*433d6423SLionel Sambuc static void port_check_cmds(struct port_state *ps)
942*433d6423SLionel Sambuc {
943*433d6423SLionel Sambuc 	/* Check what commands have completed, and finish them.
944*433d6423SLionel Sambuc 	 */
945*433d6423SLionel Sambuc 	u32_t mask, done;
946*433d6423SLionel Sambuc 	int i;
947*433d6423SLionel Sambuc 
948*433d6423SLionel Sambuc 	/* See which commands have completed. */
949*433d6423SLionel Sambuc 	if (ps->flags & FLAG_NCQ_MODE)
950*433d6423SLionel Sambuc 		mask = port_read(ps, AHCI_PORT_SACT);
951*433d6423SLionel Sambuc 	else
952*433d6423SLionel Sambuc 		mask = port_read(ps, AHCI_PORT_CI);
953*433d6423SLionel Sambuc 
954*433d6423SLionel Sambuc 	/* Wake up threads corresponding to completed commands. */
955*433d6423SLionel Sambuc 	done = ps->pend_mask & ~mask;
956*433d6423SLionel Sambuc 
957*433d6423SLionel Sambuc 	for (i = 0; i < ps->queue_depth; i++)
958*433d6423SLionel Sambuc 		if (done & (1 << i))
959*433d6423SLionel Sambuc 			port_finish_cmd(ps, i, RESULT_SUCCESS);
960*433d6423SLionel Sambuc }
961*433d6423SLionel Sambuc 
962*433d6423SLionel Sambuc /*===========================================================================*
963*433d6423SLionel Sambuc  *				port_find_cmd				     *
964*433d6423SLionel Sambuc  *===========================================================================*/
965*433d6423SLionel Sambuc static int port_find_cmd(struct port_state *ps)
966*433d6423SLionel Sambuc {
967*433d6423SLionel Sambuc 	/* Find a free command tag to queue the current request.
968*433d6423SLionel Sambuc 	 */
969*433d6423SLionel Sambuc 	int i;
970*433d6423SLionel Sambuc 
971*433d6423SLionel Sambuc 	for (i = 0; i < ps->queue_depth; i++)
972*433d6423SLionel Sambuc 		if (!(ps->pend_mask & (1 << i)))
973*433d6423SLionel Sambuc 			break;
974*433d6423SLionel Sambuc 
975*433d6423SLionel Sambuc 	/* We should always be able to find a free slot, since a thread runs
976*433d6423SLionel Sambuc 	 * only when it is free, and thus, only because a slot is available.
977*433d6423SLionel Sambuc 	 */
978*433d6423SLionel Sambuc 	assert(i < ps->queue_depth);
979*433d6423SLionel Sambuc 
980*433d6423SLionel Sambuc 	return i;
981*433d6423SLionel Sambuc }
982*433d6423SLionel Sambuc 
983*433d6423SLionel Sambuc /*===========================================================================*
984*433d6423SLionel Sambuc  *				port_get_padbuf				     *
985*433d6423SLionel Sambuc  *===========================================================================*/
986*433d6423SLionel Sambuc static int port_get_padbuf(struct port_state *ps, size_t size)
987*433d6423SLionel Sambuc {
988*433d6423SLionel Sambuc 	/* Make available a temporary buffer for use by this port. Enlarge the
989*433d6423SLionel Sambuc 	 * previous buffer if applicable and necessary, potentially changing
990*433d6423SLionel Sambuc 	 * its physical address.
991*433d6423SLionel Sambuc 	 */
992*433d6423SLionel Sambuc 
993*433d6423SLionel Sambuc 	if (ps->pad_base != NULL && ps->pad_size >= size)
994*433d6423SLionel Sambuc 		return OK;
995*433d6423SLionel Sambuc 
996*433d6423SLionel Sambuc 	if (ps->pad_base != NULL)
997*433d6423SLionel Sambuc 		free_contig(ps->pad_base, ps->pad_size);
998*433d6423SLionel Sambuc 
999*433d6423SLionel Sambuc 	ps->pad_size = size;
1000*433d6423SLionel Sambuc 	ps->pad_base = alloc_contig(ps->pad_size, 0, &ps->pad_phys);
1001*433d6423SLionel Sambuc 
1002*433d6423SLionel Sambuc 	if (ps->pad_base == NULL) {
1003*433d6423SLionel Sambuc 		dprintf(V_ERR, ("%s: unable to allocate a padding buffer of "
1004*433d6423SLionel Sambuc 			"size %lu\n", ahci_portname(ps),
1005*433d6423SLionel Sambuc 			(unsigned long) size));
1006*433d6423SLionel Sambuc 
1007*433d6423SLionel Sambuc 		return ENOMEM;
1008*433d6423SLionel Sambuc 	}
1009*433d6423SLionel Sambuc 
1010*433d6423SLionel Sambuc 	dprintf(V_INFO, ("%s: allocated padding buffer of size %lu\n",
1011*433d6423SLionel Sambuc 		ahci_portname(ps), (unsigned long) size));
1012*433d6423SLionel Sambuc 
1013*433d6423SLionel Sambuc 	return OK;
1014*433d6423SLionel Sambuc }
1015*433d6423SLionel Sambuc 
1016*433d6423SLionel Sambuc /*===========================================================================*
1017*433d6423SLionel Sambuc  *				sum_iovec				     *
1018*433d6423SLionel Sambuc  *===========================================================================*/
1019*433d6423SLionel Sambuc static int sum_iovec(struct port_state *ps, endpoint_t endpt,
1020*433d6423SLionel Sambuc 	iovec_s_t *iovec, int nr_req, vir_bytes *total)
1021*433d6423SLionel Sambuc {
1022*433d6423SLionel Sambuc 	/* Retrieve the total size of the given I/O vector. Check for alignment
1023*433d6423SLionel Sambuc 	 * requirements along the way. Return OK (and the total request size)
1024*433d6423SLionel Sambuc 	 * or an error.
1025*433d6423SLionel Sambuc 	 */
1026*433d6423SLionel Sambuc 	vir_bytes size, bytes;
1027*433d6423SLionel Sambuc 	int i;
1028*433d6423SLionel Sambuc 
1029*433d6423SLionel Sambuc 	bytes = 0;
1030*433d6423SLionel Sambuc 
1031*433d6423SLionel Sambuc 	for (i = 0; i < nr_req; i++) {
1032*433d6423SLionel Sambuc 		size = iovec[i].iov_size;
1033*433d6423SLionel Sambuc 
1034*433d6423SLionel Sambuc 		if (size == 0 || (size & 1) || size > LONG_MAX) {
1035*433d6423SLionel Sambuc 			dprintf(V_ERR, ("%s: bad size %lu in iovec from %d\n",
1036*433d6423SLionel Sambuc 				ahci_portname(ps), size, endpt));
1037*433d6423SLionel Sambuc 			return EINVAL;
1038*433d6423SLionel Sambuc 		}
1039*433d6423SLionel Sambuc 
1040*433d6423SLionel Sambuc 		bytes += size;
1041*433d6423SLionel Sambuc 
1042*433d6423SLionel Sambuc 		if (bytes > LONG_MAX) {
1043*433d6423SLionel Sambuc 			dprintf(V_ERR, ("%s: iovec size overflow from %d\n",
1044*433d6423SLionel Sambuc 				ahci_portname(ps), endpt));
1045*433d6423SLionel Sambuc 			return EINVAL;
1046*433d6423SLionel Sambuc 		}
1047*433d6423SLionel Sambuc 	}
1048*433d6423SLionel Sambuc 
1049*433d6423SLionel Sambuc 	*total = bytes;
1050*433d6423SLionel Sambuc 	return OK;
1051*433d6423SLionel Sambuc }
1052*433d6423SLionel Sambuc 
1053*433d6423SLionel Sambuc /*===========================================================================*
1054*433d6423SLionel Sambuc  *				setup_prdt				     *
1055*433d6423SLionel Sambuc  *===========================================================================*/
1056*433d6423SLionel Sambuc static int setup_prdt(struct port_state *ps, endpoint_t endpt,
1057*433d6423SLionel Sambuc 	iovec_s_t *iovec, int nr_req, vir_bytes size, vir_bytes lead,
1058*433d6423SLionel Sambuc 	int write, prd_t *prdt)
1059*433d6423SLionel Sambuc {
1060*433d6423SLionel Sambuc 	/* Convert (the first part of) an I/O vector to a Physical Region
1061*433d6423SLionel Sambuc 	 * Descriptor Table describing array that can later be used to set the
1062*433d6423SLionel Sambuc 	 * command's real PRDT. The resulting table as a whole should be
1063*433d6423SLionel Sambuc 	 * sector-aligned; leading and trailing local buffers may have to be
1064*433d6423SLionel Sambuc 	 * used for padding as appropriate. Return the number of PRD entries,
1065*433d6423SLionel Sambuc 	 * or a negative error code.
1066*433d6423SLionel Sambuc 	 */
1067*433d6423SLionel Sambuc 	struct vumap_vir vvec[NR_PRDS];
1068*433d6423SLionel Sambuc 	size_t bytes, trail;
1069*433d6423SLionel Sambuc 	int i, r, pcount, nr_prds = 0;
1070*433d6423SLionel Sambuc 
1071*433d6423SLionel Sambuc 	if (lead > 0) {
1072*433d6423SLionel Sambuc 		/* Allocate a buffer for the data we don't want. */
1073*433d6423SLionel Sambuc 		if ((r = port_get_padbuf(ps, ps->sector_size)) != OK)
1074*433d6423SLionel Sambuc 			return r;
1075*433d6423SLionel Sambuc 
1076*433d6423SLionel Sambuc 		prdt[nr_prds].vp_addr = ps->pad_phys;
1077*433d6423SLionel Sambuc 		prdt[nr_prds].vp_size = lead;
1078*433d6423SLionel Sambuc 		nr_prds++;
1079*433d6423SLionel Sambuc 	}
1080*433d6423SLionel Sambuc 
1081*433d6423SLionel Sambuc 	/* The sum of lead, size, trail has to be sector-aligned. */
1082*433d6423SLionel Sambuc 	trail = (ps->sector_size - (lead + size)) % ps->sector_size;
1083*433d6423SLionel Sambuc 
1084*433d6423SLionel Sambuc 	/* Get the physical addresses of the given buffers. */
1085*433d6423SLionel Sambuc 	for (i = 0; i < nr_req && size > 0; i++) {
1086*433d6423SLionel Sambuc 		bytes = MIN(iovec[i].iov_size, size);
1087*433d6423SLionel Sambuc 
1088*433d6423SLionel Sambuc 		if (endpt == SELF)
1089*433d6423SLionel Sambuc 			vvec[i].vv_addr = (vir_bytes) iovec[i].iov_grant;
1090*433d6423SLionel Sambuc 		else
1091*433d6423SLionel Sambuc 			vvec[i].vv_grant = iovec[i].iov_grant;
1092*433d6423SLionel Sambuc 
1093*433d6423SLionel Sambuc 		vvec[i].vv_size = bytes;
1094*433d6423SLionel Sambuc 
1095*433d6423SLionel Sambuc 		size -= bytes;
1096*433d6423SLionel Sambuc 	}
1097*433d6423SLionel Sambuc 
1098*433d6423SLionel Sambuc 	pcount = i;
1099*433d6423SLionel Sambuc 
1100*433d6423SLionel Sambuc 	if ((r = sys_vumap(endpt, vvec, i, 0, write ? VUA_READ : VUA_WRITE,
1101*433d6423SLionel Sambuc 			&prdt[nr_prds], &pcount)) != OK) {
1102*433d6423SLionel Sambuc 		dprintf(V_ERR, ("%s: unable to map memory from %d (%d)\n",
1103*433d6423SLionel Sambuc 			ahci_portname(ps), endpt, r));
1104*433d6423SLionel Sambuc 		return r;
1105*433d6423SLionel Sambuc 	}
1106*433d6423SLionel Sambuc 
1107*433d6423SLionel Sambuc 	assert(pcount > 0 && pcount <= i);
1108*433d6423SLionel Sambuc 
1109*433d6423SLionel Sambuc 	/* Make sure all buffers are physically contiguous and word-aligned. */
1110*433d6423SLionel Sambuc 	for (i = 0; i < pcount; i++) {
1111*433d6423SLionel Sambuc 		if (vvec[i].vv_size != prdt[nr_prds].vp_size) {
1112*433d6423SLionel Sambuc 			dprintf(V_ERR, ("%s: non-contiguous memory from %d\n",
1113*433d6423SLionel Sambuc 				ahci_portname(ps), endpt));
1114*433d6423SLionel Sambuc 			return EINVAL;
1115*433d6423SLionel Sambuc 		}
1116*433d6423SLionel Sambuc 
1117*433d6423SLionel Sambuc 		if (prdt[nr_prds].vp_addr & 1) {
1118*433d6423SLionel Sambuc 			dprintf(V_ERR, ("%s: bad physical address from %d\n",
1119*433d6423SLionel Sambuc 				ahci_portname(ps), endpt));
1120*433d6423SLionel Sambuc 			return EINVAL;
1121*433d6423SLionel Sambuc 		}
1122*433d6423SLionel Sambuc 
1123*433d6423SLionel Sambuc 		nr_prds++;
1124*433d6423SLionel Sambuc 	}
1125*433d6423SLionel Sambuc 
1126*433d6423SLionel Sambuc 	if (trail > 0) {
1127*433d6423SLionel Sambuc 		assert(nr_prds < NR_PRDS);
1128*433d6423SLionel Sambuc 		prdt[nr_prds].vp_addr = ps->pad_phys + lead;
1129*433d6423SLionel Sambuc 		prdt[nr_prds].vp_size = trail;
1130*433d6423SLionel Sambuc 		nr_prds++;
1131*433d6423SLionel Sambuc 	}
1132*433d6423SLionel Sambuc 
1133*433d6423SLionel Sambuc 	return nr_prds;
1134*433d6423SLionel Sambuc }
1135*433d6423SLionel Sambuc 
1136*433d6423SLionel Sambuc /*===========================================================================*
1137*433d6423SLionel Sambuc  *				port_transfer				     *
1138*433d6423SLionel Sambuc  *===========================================================================*/
1139*433d6423SLionel Sambuc static ssize_t port_transfer(struct port_state *ps, u64_t pos, u64_t eof,
1140*433d6423SLionel Sambuc 	endpoint_t endpt, iovec_s_t *iovec, int nr_req, int write, int flags)
1141*433d6423SLionel Sambuc {
1142*433d6423SLionel Sambuc 	/* Perform an I/O transfer on a port.
1143*433d6423SLionel Sambuc 	 */
1144*433d6423SLionel Sambuc 	prd_t prdt[NR_PRDS];
1145*433d6423SLionel Sambuc 	vir_bytes size, lead;
1146*433d6423SLionel Sambuc 	unsigned int count, nr_prds;
1147*433d6423SLionel Sambuc 	u64_t start_lba;
1148*433d6423SLionel Sambuc 	int r, cmd;
1149*433d6423SLionel Sambuc 
1150*433d6423SLionel Sambuc 	/* Get the total request size from the I/O vector. */
1151*433d6423SLionel Sambuc 	if ((r = sum_iovec(ps, endpt, iovec, nr_req, &size)) != OK)
1152*433d6423SLionel Sambuc 		return r;
1153*433d6423SLionel Sambuc 
1154*433d6423SLionel Sambuc 	dprintf(V_REQ, ("%s: %s for %lu bytes at pos %llx\n",
1155*433d6423SLionel Sambuc 		ahci_portname(ps), write ? "write" : "read", size, pos));
1156*433d6423SLionel Sambuc 
1157*433d6423SLionel Sambuc 	assert(ps->state == STATE_GOOD_DEV);
1158*433d6423SLionel Sambuc 	assert(ps->flags & FLAG_HAS_MEDIUM);
1159*433d6423SLionel Sambuc 	assert(ps->sector_size > 0);
1160*433d6423SLionel Sambuc 
1161*433d6423SLionel Sambuc 	/* Limit the maximum size of a single transfer.
1162*433d6423SLionel Sambuc 	 * See the comments at the top of this file for details.
1163*433d6423SLionel Sambuc 	 */
1164*433d6423SLionel Sambuc 	if (size > MAX_TRANSFER)
1165*433d6423SLionel Sambuc 		size = MAX_TRANSFER;
1166*433d6423SLionel Sambuc 
1167*433d6423SLionel Sambuc 	/* If necessary, reduce the request size so that the request does not
1168*433d6423SLionel Sambuc 	 * extend beyond the end of the partition. The caller already
1169*433d6423SLionel Sambuc 	 * guarantees that the starting position lies within the partition.
1170*433d6423SLionel Sambuc 	 */
1171*433d6423SLionel Sambuc 	if (pos + size > eof)
1172*433d6423SLionel Sambuc 		size = (vir_bytes) (eof - pos);
1173*433d6423SLionel Sambuc 
1174*433d6423SLionel Sambuc 	start_lba = pos / ps->sector_size;
1175*433d6423SLionel Sambuc 	lead = (vir_bytes) (pos % ps->sector_size);
1176*433d6423SLionel Sambuc 	count = (lead + size + ps->sector_size - 1) / ps->sector_size;
1177*433d6423SLionel Sambuc 
1178*433d6423SLionel Sambuc 	/* Position must be word-aligned for read requests, and sector-aligned
1179*433d6423SLionel Sambuc 	 * for write requests. We do not support read-modify-write for writes.
1180*433d6423SLionel Sambuc 	 */
1181*433d6423SLionel Sambuc 	if ((lead & 1) || (write && lead != 0)) {
1182*433d6423SLionel Sambuc 		dprintf(V_ERR, ("%s: unaligned position from %d\n",
1183*433d6423SLionel Sambuc 			ahci_portname(ps), endpt));
1184*433d6423SLionel Sambuc 		return EINVAL;
1185*433d6423SLionel Sambuc 	}
1186*433d6423SLionel Sambuc 
1187*433d6423SLionel Sambuc 	/* Write requests must be sector-aligned. Word alignment of the size is
1188*433d6423SLionel Sambuc 	 * already guaranteed by sum_iovec().
1189*433d6423SLionel Sambuc 	 */
1190*433d6423SLionel Sambuc 	if (write && (size % ps->sector_size) != 0) {
1191*433d6423SLionel Sambuc 		dprintf(V_ERR, ("%s: unaligned size %lu from %d\n",
1192*433d6423SLionel Sambuc 			ahci_portname(ps), size, endpt));
1193*433d6423SLionel Sambuc 		return EINVAL;
1194*433d6423SLionel Sambuc 	}
1195*433d6423SLionel Sambuc 
1196*433d6423SLionel Sambuc 	/* Create a vector of physical addresses and sizes for the transfer. */
1197*433d6423SLionel Sambuc 	nr_prds = r = setup_prdt(ps, endpt, iovec, nr_req, size, lead, write,
1198*433d6423SLionel Sambuc 		prdt);
1199*433d6423SLionel Sambuc 
1200*433d6423SLionel Sambuc 	if (r < 0) return r;
1201*433d6423SLionel Sambuc 
1202*433d6423SLionel Sambuc 	/* Perform the actual transfer. */
1203*433d6423SLionel Sambuc 	cmd = port_find_cmd(ps);
1204*433d6423SLionel Sambuc 
1205*433d6423SLionel Sambuc 	if (ps->flags & FLAG_ATAPI)
1206*433d6423SLionel Sambuc 		r = atapi_transfer(ps, cmd, start_lba, count, write, prdt,
1207*433d6423SLionel Sambuc 			nr_prds);
1208*433d6423SLionel Sambuc 	else
1209*433d6423SLionel Sambuc 		r = ata_transfer(ps, cmd, start_lba, count, write,
1210*433d6423SLionel Sambuc 			!!(flags & BDEV_FORCEWRITE), prdt, nr_prds);
1211*433d6423SLionel Sambuc 
1212*433d6423SLionel Sambuc 	if (r != OK) return r;
1213*433d6423SLionel Sambuc 
1214*433d6423SLionel Sambuc 	return size;
1215*433d6423SLionel Sambuc }
1216*433d6423SLionel Sambuc 
1217*433d6423SLionel Sambuc /*===========================================================================*
1218*433d6423SLionel Sambuc  *				port_hardreset				     *
1219*433d6423SLionel Sambuc  *===========================================================================*/
1220*433d6423SLionel Sambuc static void port_hardreset(struct port_state *ps)
1221*433d6423SLionel Sambuc {
1222*433d6423SLionel Sambuc 	/* Perform a port-level (hard) reset on the given port.
1223*433d6423SLionel Sambuc 	 */
1224*433d6423SLionel Sambuc 
1225*433d6423SLionel Sambuc 	port_write(ps, AHCI_PORT_SCTL, AHCI_PORT_SCTL_DET_INIT);
1226*433d6423SLionel Sambuc 
1227*433d6423SLionel Sambuc 	micro_delay(COMRESET_DELAY * 1000);	/* COMRESET_DELAY is in ms */
1228*433d6423SLionel Sambuc 
1229*433d6423SLionel Sambuc 	port_write(ps, AHCI_PORT_SCTL, AHCI_PORT_SCTL_DET_NONE);
1230*433d6423SLionel Sambuc }
1231*433d6423SLionel Sambuc 
1232*433d6423SLionel Sambuc /*===========================================================================*
1233*433d6423SLionel Sambuc  *				port_override				     *
1234*433d6423SLionel Sambuc  *===========================================================================*/
1235*433d6423SLionel Sambuc static void port_override(struct port_state *ps)
1236*433d6423SLionel Sambuc {
1237*433d6423SLionel Sambuc 	/* Override the port's BSY and/or DRQ flags. This may only be done
1238*433d6423SLionel Sambuc 	 * prior to starting the port.
1239*433d6423SLionel Sambuc 	 */
1240*433d6423SLionel Sambuc 	u32_t cmd;
1241*433d6423SLionel Sambuc 
1242*433d6423SLionel Sambuc 	cmd = port_read(ps, AHCI_PORT_CMD);
1243*433d6423SLionel Sambuc 	port_write(ps, AHCI_PORT_CMD, cmd | AHCI_PORT_CMD_CLO);
1244*433d6423SLionel Sambuc 
1245*433d6423SLionel Sambuc 	SPIN_UNTIL(!(port_read(ps, AHCI_PORT_CMD) & AHCI_PORT_CMD_CLO),
1246*433d6423SLionel Sambuc 		PORTREG_DELAY);
1247*433d6423SLionel Sambuc 
1248*433d6423SLionel Sambuc 	dprintf(V_INFO, ("%s: overridden\n", ahci_portname(ps)));
1249*433d6423SLionel Sambuc }
1250*433d6423SLionel Sambuc 
1251*433d6423SLionel Sambuc /*===========================================================================*
1252*433d6423SLionel Sambuc  *				port_start				     *
1253*433d6423SLionel Sambuc  *===========================================================================*/
1254*433d6423SLionel Sambuc static void port_start(struct port_state *ps)
1255*433d6423SLionel Sambuc {
1256*433d6423SLionel Sambuc 	/* Start the given port, allowing for the execution of commands and the
1257*433d6423SLionel Sambuc 	 * transfer of data on that port.
1258*433d6423SLionel Sambuc 	 */
1259*433d6423SLionel Sambuc 	u32_t cmd;
1260*433d6423SLionel Sambuc 
1261*433d6423SLionel Sambuc 	/* Reset status registers. */
1262*433d6423SLionel Sambuc 	port_write(ps, AHCI_PORT_SERR, ~0);
1263*433d6423SLionel Sambuc 	port_write(ps, AHCI_PORT_IS, ~0);
1264*433d6423SLionel Sambuc 
1265*433d6423SLionel Sambuc 	/* Start the port. */
1266*433d6423SLionel Sambuc 	cmd = port_read(ps, AHCI_PORT_CMD);
1267*433d6423SLionel Sambuc 	port_write(ps, AHCI_PORT_CMD, cmd | AHCI_PORT_CMD_ST);
1268*433d6423SLionel Sambuc 
1269*433d6423SLionel Sambuc 	dprintf(V_INFO, ("%s: started\n", ahci_portname(ps)));
1270*433d6423SLionel Sambuc }
1271*433d6423SLionel Sambuc 
1272*433d6423SLionel Sambuc /*===========================================================================*
1273*433d6423SLionel Sambuc  *				port_stop				     *
1274*433d6423SLionel Sambuc  *===========================================================================*/
1275*433d6423SLionel Sambuc static void port_stop(struct port_state *ps)
1276*433d6423SLionel Sambuc {
1277*433d6423SLionel Sambuc 	/* Stop the given port, if not already stopped.
1278*433d6423SLionel Sambuc 	 */
1279*433d6423SLionel Sambuc 	u32_t cmd;
1280*433d6423SLionel Sambuc 
1281*433d6423SLionel Sambuc 	cmd = port_read(ps, AHCI_PORT_CMD);
1282*433d6423SLionel Sambuc 
1283*433d6423SLionel Sambuc 	if (cmd & (AHCI_PORT_CMD_CR | AHCI_PORT_CMD_ST)) {
1284*433d6423SLionel Sambuc 		port_write(ps, AHCI_PORT_CMD, cmd & ~AHCI_PORT_CMD_ST);
1285*433d6423SLionel Sambuc 
1286*433d6423SLionel Sambuc 		SPIN_UNTIL(!(port_read(ps, AHCI_PORT_CMD) & AHCI_PORT_CMD_CR),
1287*433d6423SLionel Sambuc 			PORTREG_DELAY);
1288*433d6423SLionel Sambuc 
1289*433d6423SLionel Sambuc 		dprintf(V_INFO, ("%s: stopped\n", ahci_portname(ps)));
1290*433d6423SLionel Sambuc 	}
1291*433d6423SLionel Sambuc }
1292*433d6423SLionel Sambuc 
1293*433d6423SLionel Sambuc /*===========================================================================*
1294*433d6423SLionel Sambuc  *				port_restart				     *
1295*433d6423SLionel Sambuc  *===========================================================================*/
1296*433d6423SLionel Sambuc static void port_restart(struct port_state *ps)
1297*433d6423SLionel Sambuc {
1298*433d6423SLionel Sambuc 	/* Restart a port after a fatal error has occurred.
1299*433d6423SLionel Sambuc 	 */
1300*433d6423SLionel Sambuc 
1301*433d6423SLionel Sambuc 	/* Fail all outstanding commands. */
1302*433d6423SLionel Sambuc 	port_fail_cmds(ps);
1303*433d6423SLionel Sambuc 
1304*433d6423SLionel Sambuc 	/* Stop the port. */
1305*433d6423SLionel Sambuc 	port_stop(ps);
1306*433d6423SLionel Sambuc 
1307*433d6423SLionel Sambuc 	/* If the BSY and/or DRQ flags are set, reset the port. */
1308*433d6423SLionel Sambuc 	if (port_read(ps, AHCI_PORT_TFD) &
1309*433d6423SLionel Sambuc 		(AHCI_PORT_TFD_STS_BSY | AHCI_PORT_TFD_STS_DRQ)) {
1310*433d6423SLionel Sambuc 
1311*433d6423SLionel Sambuc 		dprintf(V_ERR, ("%s: port reset\n", ahci_portname(ps)));
1312*433d6423SLionel Sambuc 
1313*433d6423SLionel Sambuc 		/* To keep this driver simple, we do not transparently recover
1314*433d6423SLionel Sambuc 		 * ongoing requests. Instead, we mark the failing device as
1315*433d6423SLionel Sambuc 		 * disconnected, and reset it. If the reset succeeds, the
1316*433d6423SLionel Sambuc 		 * device (or, perhaps, eventually, another device) will come
1317*433d6423SLionel Sambuc 		 * back up. Any current and future requests to this port will
1318*433d6423SLionel Sambuc 		 * be failed until the port is fully closed and reopened.
1319*433d6423SLionel Sambuc 		 */
1320*433d6423SLionel Sambuc 		port_disconnect(ps);
1321*433d6423SLionel Sambuc 
1322*433d6423SLionel Sambuc 		/* Trigger a port reset. */
1323*433d6423SLionel Sambuc 		port_hardreset(ps);
1324*433d6423SLionel Sambuc 
1325*433d6423SLionel Sambuc 		return;
1326*433d6423SLionel Sambuc 	}
1327*433d6423SLionel Sambuc 
1328*433d6423SLionel Sambuc 	/* Start the port. */
1329*433d6423SLionel Sambuc 	port_start(ps);
1330*433d6423SLionel Sambuc }
1331*433d6423SLionel Sambuc 
1332*433d6423SLionel Sambuc /*===========================================================================*
1333*433d6423SLionel Sambuc  *				print_string				     *
1334*433d6423SLionel Sambuc  *===========================================================================*/
1335*433d6423SLionel Sambuc static void print_string(u16_t *buf, int start, int end)
1336*433d6423SLionel Sambuc {
1337*433d6423SLionel Sambuc 	/* Print a string that is stored as little-endian words and padded with
1338*433d6423SLionel Sambuc 	 * trailing spaces.
1339*433d6423SLionel Sambuc 	 */
1340*433d6423SLionel Sambuc 	int i, last = 0;
1341*433d6423SLionel Sambuc 
1342*433d6423SLionel Sambuc 	while (end >= start && buf[end] == 0x2020) end--;
1343*433d6423SLionel Sambuc 
1344*433d6423SLionel Sambuc 	if (end >= start && (buf[end] & 0xFF) == 0x20) end--, last++;
1345*433d6423SLionel Sambuc 
1346*433d6423SLionel Sambuc 	for (i = start; i <= end; i++)
1347*433d6423SLionel Sambuc 		printf("%c%c", buf[i] >> 8, buf[i] & 0xFF);
1348*433d6423SLionel Sambuc 
1349*433d6423SLionel Sambuc 	if (last)
1350*433d6423SLionel Sambuc 		printf("%c", buf[i] >> 8);
1351*433d6423SLionel Sambuc }
1352*433d6423SLionel Sambuc 
1353*433d6423SLionel Sambuc /*===========================================================================*
1354*433d6423SLionel Sambuc  *				port_id_check				     *
1355*433d6423SLionel Sambuc  *===========================================================================*/
1356*433d6423SLionel Sambuc static void port_id_check(struct port_state *ps, int success)
1357*433d6423SLionel Sambuc {
1358*433d6423SLionel Sambuc 	/* The device identification command has either completed or timed out.
1359*433d6423SLionel Sambuc 	 * Decide whether this device is usable or not, and store some of its
1360*433d6423SLionel Sambuc 	 * properties.
1361*433d6423SLionel Sambuc 	 */
1362*433d6423SLionel Sambuc 	u16_t *buf;
1363*433d6423SLionel Sambuc 
1364*433d6423SLionel Sambuc 	assert(ps->state == STATE_WAIT_ID);
1365*433d6423SLionel Sambuc 
1366*433d6423SLionel Sambuc 	ps->flags &= ~FLAG_BUSY;
1367*433d6423SLionel Sambuc 	cancel_timer(&ps->cmd_info[0].timer);
1368*433d6423SLionel Sambuc 
1369*433d6423SLionel Sambuc 	if (!success) {
1370*433d6423SLionel Sambuc 		if (!(ps->flags & FLAG_ATAPI) &&
1371*433d6423SLionel Sambuc 				port_read(ps, AHCI_PORT_SIG) != ATA_SIG_ATA) {
1372*433d6423SLionel Sambuc 			dprintf(V_INFO, ("%s: may not be ATA, trying ATAPI\n",
1373*433d6423SLionel Sambuc 				ahci_portname(ps)));
1374*433d6423SLionel Sambuc 
1375*433d6423SLionel Sambuc 			ps->flags |= FLAG_ATAPI;
1376*433d6423SLionel Sambuc 
1377*433d6423SLionel Sambuc 			(void) gen_identify(ps, FALSE /*blocking*/);
1378*433d6423SLionel Sambuc 			return;
1379*433d6423SLionel Sambuc 		}
1380*433d6423SLionel Sambuc 
1381*433d6423SLionel Sambuc 		dprintf(V_ERR,
1382*433d6423SLionel Sambuc 			("%s: unable to identify\n", ahci_portname(ps)));
1383*433d6423SLionel Sambuc 	}
1384*433d6423SLionel Sambuc 
1385*433d6423SLionel Sambuc 	/* If the identify command itself succeeded, check the results and
1386*433d6423SLionel Sambuc 	 * store some properties.
1387*433d6423SLionel Sambuc 	 */
1388*433d6423SLionel Sambuc 	if (success) {
1389*433d6423SLionel Sambuc 		buf = (u16_t *) ps->tmp_base;
1390*433d6423SLionel Sambuc 
1391*433d6423SLionel Sambuc 		if (ps->flags & FLAG_ATAPI)
1392*433d6423SLionel Sambuc 			success = atapi_id_check(ps, buf);
1393*433d6423SLionel Sambuc 		else
1394*433d6423SLionel Sambuc 			success = ata_id_check(ps, buf);
1395*433d6423SLionel Sambuc 	}
1396*433d6423SLionel Sambuc 
1397*433d6423SLionel Sambuc 	/* If the device has not been identified successfully, mark it as an
1398*433d6423SLionel Sambuc 	 * unusable device.
1399*433d6423SLionel Sambuc 	 */
1400*433d6423SLionel Sambuc 	if (!success) {
1401*433d6423SLionel Sambuc 		port_stop(ps);
1402*433d6423SLionel Sambuc 
1403*433d6423SLionel Sambuc 		ps->state = STATE_BAD_DEV;
1404*433d6423SLionel Sambuc 		port_write(ps, AHCI_PORT_IE, AHCI_PORT_IE_PRCE);
1405*433d6423SLionel Sambuc 
1406*433d6423SLionel Sambuc 		return;
1407*433d6423SLionel Sambuc 	}
1408*433d6423SLionel Sambuc 
1409*433d6423SLionel Sambuc 	/* The device has been identified successfully, and hence usable. */
1410*433d6423SLionel Sambuc 	ps->state = STATE_GOOD_DEV;
1411*433d6423SLionel Sambuc 
1412*433d6423SLionel Sambuc 	/* Print some information about the device. */
1413*433d6423SLionel Sambuc 	if (ahci_verbose >= V_INFO) {
1414*433d6423SLionel Sambuc 		printf("%s: ATA%s, ", ahci_portname(ps),
1415*433d6423SLionel Sambuc 			(ps->flags & FLAG_ATAPI) ? "PI" : "");
1416*433d6423SLionel Sambuc 		print_string(buf, 27, 46);
1417*433d6423SLionel Sambuc 		if (ahci_verbose >= V_DEV) {
1418*433d6423SLionel Sambuc 			printf(" (");
1419*433d6423SLionel Sambuc 			print_string(buf, 10, 19);
1420*433d6423SLionel Sambuc 			printf(", ");
1421*433d6423SLionel Sambuc 			print_string(buf, 23, 26);
1422*433d6423SLionel Sambuc 			printf(")");
1423*433d6423SLionel Sambuc 		}
1424*433d6423SLionel Sambuc 
1425*433d6423SLionel Sambuc 		if (ps->flags & FLAG_HAS_MEDIUM)
1426*433d6423SLionel Sambuc 			printf(", %u byte sectors, %llu MB size",
1427*433d6423SLionel Sambuc 				ps->sector_size,
1428*433d6423SLionel Sambuc 				ps->lba_count * ps->sector_size / (1024*1024));
1429*433d6423SLionel Sambuc 
1430*433d6423SLionel Sambuc 		printf("\n");
1431*433d6423SLionel Sambuc 	}
1432*433d6423SLionel Sambuc }
1433*433d6423SLionel Sambuc 
1434*433d6423SLionel Sambuc /*===========================================================================*
1435*433d6423SLionel Sambuc  *				port_connect				     *
1436*433d6423SLionel Sambuc  *===========================================================================*/
1437*433d6423SLionel Sambuc static void port_connect(struct port_state *ps)
1438*433d6423SLionel Sambuc {
1439*433d6423SLionel Sambuc 	/* A device has been found to be attached to this port. Start the port,
1440*433d6423SLionel Sambuc 	 * and do timed polling for its signature to become available.
1441*433d6423SLionel Sambuc 	 */
1442*433d6423SLionel Sambuc 	u32_t status, sig;
1443*433d6423SLionel Sambuc 
1444*433d6423SLionel Sambuc 	dprintf(V_INFO, ("%s: device connected\n", ahci_portname(ps)));
1445*433d6423SLionel Sambuc 
1446*433d6423SLionel Sambuc 	port_start(ps);
1447*433d6423SLionel Sambuc 
1448*433d6423SLionel Sambuc 	/* The next check covers a purely hypothetical race condition, where
1449*433d6423SLionel Sambuc 	 * the device would disappear right before we try to start it. This is
1450*433d6423SLionel Sambuc 	 * possible because we have to clear PxSERR, and with that, the DIAG.N
1451*433d6423SLionel Sambuc 	 * bit. Double-check the port status, and if it is not as we expect,
1452*433d6423SLionel Sambuc 	 * infer a disconnection.
1453*433d6423SLionel Sambuc 	 */
1454*433d6423SLionel Sambuc 	status = port_read(ps, AHCI_PORT_SSTS) & AHCI_PORT_SSTS_DET_MASK;
1455*433d6423SLionel Sambuc 
1456*433d6423SLionel Sambuc 	if (status != AHCI_PORT_SSTS_DET_PHY) {
1457*433d6423SLionel Sambuc 		dprintf(V_ERR, ("%s: device vanished!\n", ahci_portname(ps)));
1458*433d6423SLionel Sambuc 
1459*433d6423SLionel Sambuc 		port_stop(ps);
1460*433d6423SLionel Sambuc 
1461*433d6423SLionel Sambuc 		ps->state = STATE_NO_DEV;
1462*433d6423SLionel Sambuc 		ps->flags &= ~FLAG_BUSY;
1463*433d6423SLionel Sambuc 
1464*433d6423SLionel Sambuc 		return;
1465*433d6423SLionel Sambuc 	}
1466*433d6423SLionel Sambuc 
1467*433d6423SLionel Sambuc 	/* Clear all state flags except the busy flag, which may be relevant if
1468*433d6423SLionel Sambuc 	 * a BDEV_OPEN call is waiting for the device to become ready; the
1469*433d6423SLionel Sambuc 	 * barrier flag, which prevents access to the device until it is
1470*433d6423SLionel Sambuc 	 * completely closed and (re)opened; and, the thread suspension flag.
1471*433d6423SLionel Sambuc 	 */
1472*433d6423SLionel Sambuc 	ps->flags &= (FLAG_BUSY | FLAG_BARRIER | FLAG_SUSPENDED);
1473*433d6423SLionel Sambuc 
1474*433d6423SLionel Sambuc 	/* Check the port's signature. We only use the signature to speed up
1475*433d6423SLionel Sambuc 	 * identification; we will try both ATA and ATAPI if the signature is
1476*433d6423SLionel Sambuc 	 * neither ATA nor ATAPI.
1477*433d6423SLionel Sambuc 	 */
1478*433d6423SLionel Sambuc 	sig = port_read(ps, AHCI_PORT_SIG);
1479*433d6423SLionel Sambuc 
1480*433d6423SLionel Sambuc 	if (sig == ATA_SIG_ATAPI)
1481*433d6423SLionel Sambuc 		ps->flags |= FLAG_ATAPI;
1482*433d6423SLionel Sambuc 
1483*433d6423SLionel Sambuc 	/* Attempt to identify the device. Do this using continuation, because
1484*433d6423SLionel Sambuc 	 * we may already be called from port_wait() here, and could end up
1485*433d6423SLionel Sambuc 	 * confusing the timer expiration procedure.
1486*433d6423SLionel Sambuc 	 */
1487*433d6423SLionel Sambuc 	ps->state = STATE_WAIT_ID;
1488*433d6423SLionel Sambuc 	port_write(ps, AHCI_PORT_IE, AHCI_PORT_IE_MASK);
1489*433d6423SLionel Sambuc 
1490*433d6423SLionel Sambuc 	(void) gen_identify(ps, FALSE /*blocking*/);
1491*433d6423SLionel Sambuc }
1492*433d6423SLionel Sambuc 
1493*433d6423SLionel Sambuc /*===========================================================================*
1494*433d6423SLionel Sambuc  *				port_disconnect				     *
1495*433d6423SLionel Sambuc  *===========================================================================*/
1496*433d6423SLionel Sambuc static void port_disconnect(struct port_state *ps)
1497*433d6423SLionel Sambuc {
1498*433d6423SLionel Sambuc 	/* The device has detached from this port. It has already been stopped.
1499*433d6423SLionel Sambuc 	 */
1500*433d6423SLionel Sambuc 
1501*433d6423SLionel Sambuc 	dprintf(V_INFO, ("%s: device disconnected\n", ahci_portname(ps)));
1502*433d6423SLionel Sambuc 
1503*433d6423SLionel Sambuc 	ps->state = STATE_NO_DEV;
1504*433d6423SLionel Sambuc 	port_write(ps, AHCI_PORT_IE, AHCI_PORT_IE_PCE);
1505*433d6423SLionel Sambuc 	ps->flags &= ~FLAG_BUSY;
1506*433d6423SLionel Sambuc 
1507*433d6423SLionel Sambuc 	/* Fail any ongoing request. The caller may already have done this. */
1508*433d6423SLionel Sambuc 	port_fail_cmds(ps);
1509*433d6423SLionel Sambuc 
1510*433d6423SLionel Sambuc 	/* Block any further access until the device is completely closed and
1511*433d6423SLionel Sambuc 	 * reopened. This prevents arbitrary I/O to a newly plugged-in device
1512*433d6423SLionel Sambuc 	 * without upper layers noticing.
1513*433d6423SLionel Sambuc 	 */
1514*433d6423SLionel Sambuc 	ps->flags |= FLAG_BARRIER;
1515*433d6423SLionel Sambuc 
1516*433d6423SLionel Sambuc 	/* Inform the blockdriver library to reduce the number of threads. */
1517*433d6423SLionel Sambuc 	blockdriver_mt_set_workers(ps->device, 1);
1518*433d6423SLionel Sambuc }
1519*433d6423SLionel Sambuc 
1520*433d6423SLionel Sambuc /*===========================================================================*
1521*433d6423SLionel Sambuc  *				port_dev_check				     *
1522*433d6423SLionel Sambuc  *===========================================================================*/
1523*433d6423SLionel Sambuc static void port_dev_check(struct port_state *ps)
1524*433d6423SLionel Sambuc {
1525*433d6423SLionel Sambuc 	/* Perform device detection by means of polling.
1526*433d6423SLionel Sambuc 	 */
1527*433d6423SLionel Sambuc 	u32_t status, tfd;
1528*433d6423SLionel Sambuc 
1529*433d6423SLionel Sambuc 	assert(ps->state == STATE_WAIT_DEV);
1530*433d6423SLionel Sambuc 
1531*433d6423SLionel Sambuc 	status = port_read(ps, AHCI_PORT_SSTS) & AHCI_PORT_SSTS_DET_MASK;
1532*433d6423SLionel Sambuc 
1533*433d6423SLionel Sambuc 	dprintf(V_DEV, ("%s: polled status %u\n", ahci_portname(ps), status));
1534*433d6423SLionel Sambuc 
1535*433d6423SLionel Sambuc 	switch (status) {
1536*433d6423SLionel Sambuc 	case AHCI_PORT_SSTS_DET_PHY:
1537*433d6423SLionel Sambuc 		tfd = port_read(ps, AHCI_PORT_TFD);
1538*433d6423SLionel Sambuc 
1539*433d6423SLionel Sambuc 		/* If a Phy connection has been established, and the BSY and
1540*433d6423SLionel Sambuc 		 * DRQ flags are cleared, the device is ready.
1541*433d6423SLionel Sambuc 		 */
1542*433d6423SLionel Sambuc 		if (!(tfd & (AHCI_PORT_TFD_STS_BSY | AHCI_PORT_TFD_STS_DRQ))) {
1543*433d6423SLionel Sambuc 			port_connect(ps);
1544*433d6423SLionel Sambuc 
1545*433d6423SLionel Sambuc 			return;
1546*433d6423SLionel Sambuc 		}
1547*433d6423SLionel Sambuc 
1548*433d6423SLionel Sambuc 		/* fall-through */
1549*433d6423SLionel Sambuc 	case AHCI_PORT_SSTS_DET_DET:
1550*433d6423SLionel Sambuc 		/* A device has been detected, but it is not ready yet. Try for
1551*433d6423SLionel Sambuc 		 * a while before giving up. This may take seconds.
1552*433d6423SLionel Sambuc 		 */
1553*433d6423SLionel Sambuc 		if (ps->left > 0) {
1554*433d6423SLionel Sambuc 			ps->left--;
1555*433d6423SLionel Sambuc 			set_timer(&ps->cmd_info[0].timer, ahci_device_delay,
1556*433d6423SLionel Sambuc 				port_timeout, BUILD_ARG(ps - port_state, 0));
1557*433d6423SLionel Sambuc 			return;
1558*433d6423SLionel Sambuc 		}
1559*433d6423SLionel Sambuc 	}
1560*433d6423SLionel Sambuc 
1561*433d6423SLionel Sambuc 	dprintf(V_INFO, ("%s: device not ready\n", ahci_portname(ps)));
1562*433d6423SLionel Sambuc 
1563*433d6423SLionel Sambuc 	/* We get here on timeout, and if the HBA reports that there is no
1564*433d6423SLionel Sambuc 	 * device present at all. In all cases, we change to another state.
1565*433d6423SLionel Sambuc 	 */
1566*433d6423SLionel Sambuc 	if (status == AHCI_PORT_SSTS_DET_PHY) {
1567*433d6423SLionel Sambuc 		/* Some devices may not correctly clear BSY/DRQ. Upon timeout,
1568*433d6423SLionel Sambuc 		 * if we can override these flags, do so and start the
1569*433d6423SLionel Sambuc 		 * identification process anyway.
1570*433d6423SLionel Sambuc 		 */
1571*433d6423SLionel Sambuc 		if (hba_state.has_clo) {
1572*433d6423SLionel Sambuc 			port_override(ps);
1573*433d6423SLionel Sambuc 
1574*433d6423SLionel Sambuc 			port_connect(ps);
1575*433d6423SLionel Sambuc 
1576*433d6423SLionel Sambuc 			return;
1577*433d6423SLionel Sambuc 		}
1578*433d6423SLionel Sambuc 
1579*433d6423SLionel Sambuc 		/* A device is present and initialized, but not ready. */
1580*433d6423SLionel Sambuc 		ps->state = STATE_BAD_DEV;
1581*433d6423SLionel Sambuc 		port_write(ps, AHCI_PORT_IE, AHCI_PORT_IE_PRCE);
1582*433d6423SLionel Sambuc 	} else {
1583*433d6423SLionel Sambuc 		/* A device may or may not be present, but it does not appear
1584*433d6423SLionel Sambuc 		 * to be ready in any case. Ignore it until the next device
1585*433d6423SLionel Sambuc 		 * initialization event.
1586*433d6423SLionel Sambuc 		 */
1587*433d6423SLionel Sambuc 		ps->state = STATE_NO_DEV;
1588*433d6423SLionel Sambuc 		ps->flags &= ~FLAG_BUSY;
1589*433d6423SLionel Sambuc 	}
1590*433d6423SLionel Sambuc }
1591*433d6423SLionel Sambuc 
1592*433d6423SLionel Sambuc /*===========================================================================*
1593*433d6423SLionel Sambuc  *				port_intr				     *
1594*433d6423SLionel Sambuc  *===========================================================================*/
1595*433d6423SLionel Sambuc static void port_intr(struct port_state *ps)
1596*433d6423SLionel Sambuc {
1597*433d6423SLionel Sambuc 	/* Process an interrupt on this port.
1598*433d6423SLionel Sambuc 	 */
1599*433d6423SLionel Sambuc 	u32_t smask, emask;
1600*433d6423SLionel Sambuc 	int success;
1601*433d6423SLionel Sambuc 
1602*433d6423SLionel Sambuc 	if (ps->state == STATE_NO_PORT) {
1603*433d6423SLionel Sambuc 		dprintf(V_ERR, ("%s: interrupt for invalid port!\n",
1604*433d6423SLionel Sambuc 			ahci_portname(ps)));
1605*433d6423SLionel Sambuc 
1606*433d6423SLionel Sambuc 		return;
1607*433d6423SLionel Sambuc 	}
1608*433d6423SLionel Sambuc 
1609*433d6423SLionel Sambuc 	smask = port_read(ps, AHCI_PORT_IS);
1610*433d6423SLionel Sambuc 	emask = smask & port_read(ps, AHCI_PORT_IE);
1611*433d6423SLionel Sambuc 
1612*433d6423SLionel Sambuc 	/* Clear the interrupt flags that we saw were set. */
1613*433d6423SLionel Sambuc 	port_write(ps, AHCI_PORT_IS, smask);
1614*433d6423SLionel Sambuc 
1615*433d6423SLionel Sambuc 	dprintf(V_REQ, ("%s: interrupt (%08x)\n", ahci_portname(ps), smask));
1616*433d6423SLionel Sambuc 
1617*433d6423SLionel Sambuc 	/* Check if any commands have completed. */
1618*433d6423SLionel Sambuc 	port_check_cmds(ps);
1619*433d6423SLionel Sambuc 
1620*433d6423SLionel Sambuc 	if (emask & AHCI_PORT_IS_PCS) {
1621*433d6423SLionel Sambuc 		/* Clear the X diagnostics bit to clear this interrupt. */
1622*433d6423SLionel Sambuc 		port_write(ps, AHCI_PORT_SERR, AHCI_PORT_SERR_DIAG_X);
1623*433d6423SLionel Sambuc 
1624*433d6423SLionel Sambuc 		dprintf(V_DEV, ("%s: device attached\n", ahci_portname(ps)));
1625*433d6423SLionel Sambuc 
1626*433d6423SLionel Sambuc 		switch (ps->state) {
1627*433d6423SLionel Sambuc 		case STATE_SPIN_UP:
1628*433d6423SLionel Sambuc 		case STATE_NO_DEV:
1629*433d6423SLionel Sambuc 			/* Reportedly, a device has shown up. Start polling its
1630*433d6423SLionel Sambuc 			 * status until it has become ready.
1631*433d6423SLionel Sambuc 			 */
1632*433d6423SLionel Sambuc 
1633*433d6423SLionel Sambuc 			if (ps->state == STATE_SPIN_UP)
1634*433d6423SLionel Sambuc 				cancel_timer(&ps->cmd_info[0].timer);
1635*433d6423SLionel Sambuc 
1636*433d6423SLionel Sambuc 			ps->state = STATE_WAIT_DEV;
1637*433d6423SLionel Sambuc 			ps->left = ahci_device_checks;
1638*433d6423SLionel Sambuc 
1639*433d6423SLionel Sambuc 			port_dev_check(ps);
1640*433d6423SLionel Sambuc 
1641*433d6423SLionel Sambuc 			break;
1642*433d6423SLionel Sambuc 
1643*433d6423SLionel Sambuc 		case STATE_WAIT_DEV:
1644*433d6423SLionel Sambuc 			/* Nothing else to do. */
1645*433d6423SLionel Sambuc 			break;
1646*433d6423SLionel Sambuc 
1647*433d6423SLionel Sambuc 		default:
1648*433d6423SLionel Sambuc 			/* Impossible. */
1649*433d6423SLionel Sambuc 			assert(0);
1650*433d6423SLionel Sambuc 		}
1651*433d6423SLionel Sambuc 	} else if (emask & AHCI_PORT_IS_PRCS) {
1652*433d6423SLionel Sambuc 		/* Clear the N diagnostics bit to clear this interrupt. */
1653*433d6423SLionel Sambuc 		port_write(ps, AHCI_PORT_SERR, AHCI_PORT_SERR_DIAG_N);
1654*433d6423SLionel Sambuc 
1655*433d6423SLionel Sambuc 		dprintf(V_DEV, ("%s: device detached\n", ahci_portname(ps)));
1656*433d6423SLionel Sambuc 
1657*433d6423SLionel Sambuc 		switch (ps->state) {
1658*433d6423SLionel Sambuc 		case STATE_WAIT_ID:
1659*433d6423SLionel Sambuc 		case STATE_GOOD_DEV:
1660*433d6423SLionel Sambuc 			/* The device is no longer ready. Stop the port, cancel
1661*433d6423SLionel Sambuc 			 * ongoing requests, and disconnect the device.
1662*433d6423SLionel Sambuc 			 */
1663*433d6423SLionel Sambuc 			port_stop(ps);
1664*433d6423SLionel Sambuc 
1665*433d6423SLionel Sambuc 			/* fall-through */
1666*433d6423SLionel Sambuc 		case STATE_BAD_DEV:
1667*433d6423SLionel Sambuc 			port_disconnect(ps);
1668*433d6423SLionel Sambuc 
1669*433d6423SLionel Sambuc 			/* The device has become unusable to us at this point.
1670*433d6423SLionel Sambuc 			 * Reset the port to make sure that once the device (or
1671*433d6423SLionel Sambuc 			 * another device) becomes usable again, we will get a
1672*433d6423SLionel Sambuc 			 * PCS interrupt as well.
1673*433d6423SLionel Sambuc 			 */
1674*433d6423SLionel Sambuc 			port_hardreset(ps);
1675*433d6423SLionel Sambuc 
1676*433d6423SLionel Sambuc 			break;
1677*433d6423SLionel Sambuc 
1678*433d6423SLionel Sambuc 		default:
1679*433d6423SLionel Sambuc 			/* Impossible. */
1680*433d6423SLionel Sambuc 			assert(0);
1681*433d6423SLionel Sambuc 		}
1682*433d6423SLionel Sambuc 	} else if (smask & AHCI_PORT_IS_MASK) {
1683*433d6423SLionel Sambuc 		/* We assume that any other interrupt indicates command
1684*433d6423SLionel Sambuc 		 * completion or (command or device) failure. Unfortunately, if
1685*433d6423SLionel Sambuc 		 * an NCQ command failed, we cannot easily determine which one
1686*433d6423SLionel Sambuc 		 * it was. For that reason, after completing all successfully
1687*433d6423SLionel Sambuc 		 * finished commands (above), we fail all other outstanding
1688*433d6423SLionel Sambuc 		 * commands and restart the port. This can possibly be improved
1689*433d6423SLionel Sambuc 		 * later by obtaining per-command status results from the HBA.
1690*433d6423SLionel Sambuc 		 */
1691*433d6423SLionel Sambuc 
1692*433d6423SLionel Sambuc 		success = !(port_read(ps, AHCI_PORT_TFD) &
1693*433d6423SLionel Sambuc 			(AHCI_PORT_TFD_STS_ERR | AHCI_PORT_TFD_STS_DF));
1694*433d6423SLionel Sambuc 
1695*433d6423SLionel Sambuc 		/* Check now for failure. There are fatal failures, and there
1696*433d6423SLionel Sambuc 		 * are failures that set the TFD.STS.ERR field using a D2H
1697*433d6423SLionel Sambuc 		 * FIS. In both cases, we just restart the port, failing all
1698*433d6423SLionel Sambuc 		 * commands in the process.
1699*433d6423SLionel Sambuc 		 */
1700*433d6423SLionel Sambuc 		if ((port_read(ps, AHCI_PORT_TFD) &
1701*433d6423SLionel Sambuc 			(AHCI_PORT_TFD_STS_ERR | AHCI_PORT_TFD_STS_DF)) ||
1702*433d6423SLionel Sambuc 			(smask & AHCI_PORT_IS_RESTART)) {
1703*433d6423SLionel Sambuc 				port_restart(ps);
1704*433d6423SLionel Sambuc 		}
1705*433d6423SLionel Sambuc 
1706*433d6423SLionel Sambuc 		/* If we were waiting for ID verification, check now. */
1707*433d6423SLionel Sambuc 		if (ps->state == STATE_WAIT_ID)
1708*433d6423SLionel Sambuc 			port_id_check(ps, success);
1709*433d6423SLionel Sambuc 	}
1710*433d6423SLionel Sambuc }
1711*433d6423SLionel Sambuc 
1712*433d6423SLionel Sambuc /*===========================================================================*
1713*433d6423SLionel Sambuc  *				port_timeout				     *
1714*433d6423SLionel Sambuc  *===========================================================================*/
1715*433d6423SLionel Sambuc static void port_timeout(minix_timer_t *tp)
1716*433d6423SLionel Sambuc {
1717*433d6423SLionel Sambuc 	/* A timeout has occurred on this port. Figure out what the timeout is
1718*433d6423SLionel Sambuc 	 * for, and take appropriate action.
1719*433d6423SLionel Sambuc 	 */
1720*433d6423SLionel Sambuc 	struct port_state *ps;
1721*433d6423SLionel Sambuc 	int port, cmd;
1722*433d6423SLionel Sambuc 
1723*433d6423SLionel Sambuc 	port = GET_PORT(tmr_arg(tp)->ta_int);
1724*433d6423SLionel Sambuc 	cmd = GET_TAG(tmr_arg(tp)->ta_int);
1725*433d6423SLionel Sambuc 
1726*433d6423SLionel Sambuc 	assert(port >= 0 && port < hba_state.nr_ports);
1727*433d6423SLionel Sambuc 
1728*433d6423SLionel Sambuc 	ps = &port_state[port];
1729*433d6423SLionel Sambuc 
1730*433d6423SLionel Sambuc 	/* Regardless of the outcome of this timeout, wake up the thread if it
1731*433d6423SLionel Sambuc 	 * is suspended. This applies only during the initialization.
1732*433d6423SLionel Sambuc 	 */
1733*433d6423SLionel Sambuc 	if (ps->flags & FLAG_SUSPENDED) {
1734*433d6423SLionel Sambuc 		assert(cmd == 0);
1735*433d6423SLionel Sambuc 		blockdriver_mt_wakeup(ps->cmd_info[0].tid);
1736*433d6423SLionel Sambuc 	}
1737*433d6423SLionel Sambuc 
1738*433d6423SLionel Sambuc 	/* If detection of a device after startup timed out, give up on initial
1739*433d6423SLionel Sambuc 	 * detection and only look for hot plug events from now on.
1740*433d6423SLionel Sambuc 	 */
1741*433d6423SLionel Sambuc 	if (ps->state == STATE_SPIN_UP) {
1742*433d6423SLionel Sambuc 		/* One exception: if the PCS interrupt bit is set here, then we
1743*433d6423SLionel Sambuc 		 * are probably running on VirtualBox, which is currently not
1744*433d6423SLionel Sambuc 		 * always raising interrupts when setting interrupt bits (!).
1745*433d6423SLionel Sambuc 		 */
1746*433d6423SLionel Sambuc 		if (port_read(ps, AHCI_PORT_IS) & AHCI_PORT_IS_PCS) {
1747*433d6423SLionel Sambuc 			dprintf(V_INFO, ("%s: bad controller, no interrupt\n",
1748*433d6423SLionel Sambuc 				ahci_portname(ps)));
1749*433d6423SLionel Sambuc 
1750*433d6423SLionel Sambuc 			ps->state = STATE_WAIT_DEV;
1751*433d6423SLionel Sambuc 			ps->left = ahci_device_checks;
1752*433d6423SLionel Sambuc 
1753*433d6423SLionel Sambuc 			port_dev_check(ps);
1754*433d6423SLionel Sambuc 
1755*433d6423SLionel Sambuc 			return;
1756*433d6423SLionel Sambuc 		} else {
1757*433d6423SLionel Sambuc 			dprintf(V_INFO, ("%s: spin-up timeout\n",
1758*433d6423SLionel Sambuc 				ahci_portname(ps)));
1759*433d6423SLionel Sambuc 
1760*433d6423SLionel Sambuc 			/* If the busy flag is set, a BDEV_OPEN request is
1761*433d6423SLionel Sambuc 			 * waiting for the detection to finish; clear the busy
1762*433d6423SLionel Sambuc 			 * flag to return an error to the caller.
1763*433d6423SLionel Sambuc 			 */
1764*433d6423SLionel Sambuc 			ps->state = STATE_NO_DEV;
1765*433d6423SLionel Sambuc 			ps->flags &= ~FLAG_BUSY;
1766*433d6423SLionel Sambuc 		}
1767*433d6423SLionel Sambuc 
1768*433d6423SLionel Sambuc 		return;
1769*433d6423SLionel Sambuc 	}
1770*433d6423SLionel Sambuc 
1771*433d6423SLionel Sambuc 	/* If we are waiting for a device to become connected and initialized,
1772*433d6423SLionel Sambuc 	 * check now.
1773*433d6423SLionel Sambuc 	 */
1774*433d6423SLionel Sambuc 	if (ps->state == STATE_WAIT_DEV) {
1775*433d6423SLionel Sambuc 		port_dev_check(ps);
1776*433d6423SLionel Sambuc 
1777*433d6423SLionel Sambuc 		return;
1778*433d6423SLionel Sambuc 	}
1779*433d6423SLionel Sambuc 
1780*433d6423SLionel Sambuc 	dprintf(V_ERR, ("%s: timeout\n", ahci_portname(ps)));
1781*433d6423SLionel Sambuc 
1782*433d6423SLionel Sambuc 	/* Restart the port, failing all current commands. */
1783*433d6423SLionel Sambuc 	port_restart(ps);
1784*433d6423SLionel Sambuc 
1785*433d6423SLionel Sambuc 	/* Finish up the identify operation. */
1786*433d6423SLionel Sambuc 	if (ps->state == STATE_WAIT_ID)
1787*433d6423SLionel Sambuc 		port_id_check(ps, FALSE);
1788*433d6423SLionel Sambuc }
1789*433d6423SLionel Sambuc 
1790*433d6423SLionel Sambuc /*===========================================================================*
1791*433d6423SLionel Sambuc  *				port_wait				     *
1792*433d6423SLionel Sambuc  *===========================================================================*/
1793*433d6423SLionel Sambuc static void port_wait(struct port_state *ps)
1794*433d6423SLionel Sambuc {
1795*433d6423SLionel Sambuc 	/* Suspend the current thread until the given port is no longer busy,
1796*433d6423SLionel Sambuc 	 * due to either command completion or timeout.
1797*433d6423SLionel Sambuc 	 */
1798*433d6423SLionel Sambuc 
1799*433d6423SLionel Sambuc 	ps->flags |= FLAG_SUSPENDED;
1800*433d6423SLionel Sambuc 
1801*433d6423SLionel Sambuc 	while (ps->flags & FLAG_BUSY)
1802*433d6423SLionel Sambuc 		blockdriver_mt_sleep();
1803*433d6423SLionel Sambuc 
1804*433d6423SLionel Sambuc 	ps->flags &= ~FLAG_SUSPENDED;
1805*433d6423SLionel Sambuc }
1806*433d6423SLionel Sambuc 
1807*433d6423SLionel Sambuc /*===========================================================================*
1808*433d6423SLionel Sambuc  *				port_issue				     *
1809*433d6423SLionel Sambuc  *===========================================================================*/
1810*433d6423SLionel Sambuc static void port_issue(struct port_state *ps, int cmd, clock_t timeout)
1811*433d6423SLionel Sambuc {
1812*433d6423SLionel Sambuc 	/* Issue a command to the port, and set a timer to trigger a timeout
1813*433d6423SLionel Sambuc 	 * if the command takes too long to complete.
1814*433d6423SLionel Sambuc 	 */
1815*433d6423SLionel Sambuc 
1816*433d6423SLionel Sambuc 	/* Set the corresponding NCQ command bit, if applicable. */
1817*433d6423SLionel Sambuc 	if (ps->flags & FLAG_HAS_NCQ)
1818*433d6423SLionel Sambuc 		port_write(ps, AHCI_PORT_SACT, 1 << cmd);
1819*433d6423SLionel Sambuc 
1820*433d6423SLionel Sambuc 	/* Make sure that the compiler does not delay any previous write
1821*433d6423SLionel Sambuc 	 * operations until after the write to the command issue register.
1822*433d6423SLionel Sambuc 	 */
1823*433d6423SLionel Sambuc 	__insn_barrier();
1824*433d6423SLionel Sambuc 
1825*433d6423SLionel Sambuc 	/* Tell the controller that a new command is ready. */
1826*433d6423SLionel Sambuc 	port_write(ps, AHCI_PORT_CI, 1 << cmd);
1827*433d6423SLionel Sambuc 
1828*433d6423SLionel Sambuc 	/* Update pending commands. */
1829*433d6423SLionel Sambuc 	ps->pend_mask |= 1 << cmd;
1830*433d6423SLionel Sambuc 
1831*433d6423SLionel Sambuc 	/* Set a timer in case the command does not complete at all. */
1832*433d6423SLionel Sambuc 	set_timer(&ps->cmd_info[cmd].timer, timeout, port_timeout,
1833*433d6423SLionel Sambuc 		BUILD_ARG(ps - port_state, cmd));
1834*433d6423SLionel Sambuc }
1835*433d6423SLionel Sambuc 
1836*433d6423SLionel Sambuc /*===========================================================================*
1837*433d6423SLionel Sambuc  *				port_exec				     *
1838*433d6423SLionel Sambuc  *===========================================================================*/
1839*433d6423SLionel Sambuc static int port_exec(struct port_state *ps, int cmd, clock_t timeout)
1840*433d6423SLionel Sambuc {
1841*433d6423SLionel Sambuc 	/* Execute a command on a port, wait for the command to complete or for
1842*433d6423SLionel Sambuc 	 * a timeout, and return whether the command succeeded or not.
1843*433d6423SLionel Sambuc 	 */
1844*433d6423SLionel Sambuc 
1845*433d6423SLionel Sambuc 	port_issue(ps, cmd, timeout);
1846*433d6423SLionel Sambuc 
1847*433d6423SLionel Sambuc 	/* Put the thread to sleep until a timeout or a command completion
1848*433d6423SLionel Sambuc 	 * happens. Earlier, we used to call port_wait which set the suspended
1849*433d6423SLionel Sambuc 	 * flag. We now abandon it since the flag has to work on a per-thread,
1850*433d6423SLionel Sambuc 	 * and hence per-tag basis and not on a per-port basis. Instead, we
1851*433d6423SLionel Sambuc 	 * retain that call only to defer open calls during device/driver
1852*433d6423SLionel Sambuc 	 * initialization. Instead, we call sleep here directly. Before
1853*433d6423SLionel Sambuc 	 * sleeping, we register the thread.
1854*433d6423SLionel Sambuc 	 */
1855*433d6423SLionel Sambuc 	ps->cmd_info[cmd].tid = blockdriver_mt_get_tid();
1856*433d6423SLionel Sambuc 
1857*433d6423SLionel Sambuc 	blockdriver_mt_sleep();
1858*433d6423SLionel Sambuc 
1859*433d6423SLionel Sambuc 	/* Cancelling a timer that just triggered, does no harm. */
1860*433d6423SLionel Sambuc 	cancel_timer(&ps->cmd_info[cmd].timer);
1861*433d6423SLionel Sambuc 
1862*433d6423SLionel Sambuc 	assert(!(ps->flags & FLAG_BUSY));
1863*433d6423SLionel Sambuc 
1864*433d6423SLionel Sambuc 	dprintf(V_REQ, ("%s: end of command -- %s\n", ahci_portname(ps),
1865*433d6423SLionel Sambuc 		(ps->cmd_info[cmd].result == RESULT_FAILURE) ?
1866*433d6423SLionel Sambuc 		"failure" : "success"));
1867*433d6423SLionel Sambuc 
1868*433d6423SLionel Sambuc 	if (ps->cmd_info[cmd].result == RESULT_FAILURE)
1869*433d6423SLionel Sambuc 		return EIO;
1870*433d6423SLionel Sambuc 
1871*433d6423SLionel Sambuc 	return OK;
1872*433d6423SLionel Sambuc }
1873*433d6423SLionel Sambuc 
1874*433d6423SLionel Sambuc /*===========================================================================*
1875*433d6423SLionel Sambuc  *				port_alloc				     *
1876*433d6423SLionel Sambuc  *===========================================================================*/
1877*433d6423SLionel Sambuc static void port_alloc(struct port_state *ps)
1878*433d6423SLionel Sambuc {
1879*433d6423SLionel Sambuc 	/* Allocate memory for the given port, and enable FIS receipt. We try
1880*433d6423SLionel Sambuc 	 * to cram everything into one 4K-page in order to limit memory usage
1881*433d6423SLionel Sambuc 	 * as much as possible. More memory may be allocated on demand later,
1882*433d6423SLionel Sambuc 	 * but allocation failure should be fatal only here. Note that we do
1883*433d6423SLionel Sambuc 	 * not allocate memory for sector padding here, because we do not know
1884*433d6423SLionel Sambuc 	 * the device's sector size yet.
1885*433d6423SLionel Sambuc 	 */
1886*433d6423SLionel Sambuc 	size_t fis_off, tmp_off, ct_off; int i;
1887*433d6423SLionel Sambuc 	size_t ct_offs[NR_CMDS];
1888*433d6423SLionel Sambuc 	u32_t cmd;
1889*433d6423SLionel Sambuc 
1890*433d6423SLionel Sambuc 	fis_off = AHCI_CL_SIZE + AHCI_FIS_SIZE - 1;
1891*433d6423SLionel Sambuc 	fis_off -= fis_off % AHCI_FIS_SIZE;
1892*433d6423SLionel Sambuc 
1893*433d6423SLionel Sambuc 	tmp_off = fis_off + AHCI_FIS_SIZE + AHCI_TMP_ALIGN - 1;
1894*433d6423SLionel Sambuc 	tmp_off -= tmp_off % AHCI_TMP_ALIGN;
1895*433d6423SLionel Sambuc 
1896*433d6423SLionel Sambuc 	/* Allocate memory for all the commands. */
1897*433d6423SLionel Sambuc 	ct_off = tmp_off + AHCI_TMP_SIZE;
1898*433d6423SLionel Sambuc 	for (i = 0; i < NR_CMDS; i++) {
1899*433d6423SLionel Sambuc 		ct_off += AHCI_CT_ALIGN - 1;
1900*433d6423SLionel Sambuc 		ct_off -= ct_off % AHCI_CT_ALIGN;
1901*433d6423SLionel Sambuc 		ct_offs[i] = ct_off;
1902*433d6423SLionel Sambuc 		ps->mem_size = ct_off + AHCI_CT_SIZE;
1903*433d6423SLionel Sambuc 		ct_off = ps->mem_size;
1904*433d6423SLionel Sambuc 	}
1905*433d6423SLionel Sambuc 
1906*433d6423SLionel Sambuc 	ps->mem_base = alloc_contig(ps->mem_size, AC_ALIGN4K, &ps->mem_phys);
1907*433d6423SLionel Sambuc 	if (ps->mem_base == NULL)
1908*433d6423SLionel Sambuc 		panic("unable to allocate port memory");
1909*433d6423SLionel Sambuc 	memset(ps->mem_base, 0, ps->mem_size);
1910*433d6423SLionel Sambuc 
1911*433d6423SLionel Sambuc 	ps->cl_base = (u32_t *) ps->mem_base;
1912*433d6423SLionel Sambuc 	ps->cl_phys = ps->mem_phys;
1913*433d6423SLionel Sambuc 	assert(ps->cl_phys % AHCI_CL_SIZE == 0);
1914*433d6423SLionel Sambuc 
1915*433d6423SLionel Sambuc 	ps->fis_base = (u32_t *) (ps->mem_base + fis_off);
1916*433d6423SLionel Sambuc 	ps->fis_phys = ps->mem_phys + fis_off;
1917*433d6423SLionel Sambuc 	assert(ps->fis_phys % AHCI_FIS_SIZE == 0);
1918*433d6423SLionel Sambuc 
1919*433d6423SLionel Sambuc 	ps->tmp_base = (u8_t *) (ps->mem_base + tmp_off);
1920*433d6423SLionel Sambuc 	ps->tmp_phys = ps->mem_phys + tmp_off;
1921*433d6423SLionel Sambuc 	assert(ps->tmp_phys % AHCI_TMP_ALIGN == 0);
1922*433d6423SLionel Sambuc 
1923*433d6423SLionel Sambuc 	for (i = 0; i < NR_CMDS; i++) {
1924*433d6423SLionel Sambuc 		ps->ct_base[i] = ps->mem_base + ct_offs[i];
1925*433d6423SLionel Sambuc 		ps->ct_phys[i] = ps->mem_phys + ct_offs[i];
1926*433d6423SLionel Sambuc 		assert(ps->ct_phys[i] % AHCI_CT_ALIGN == 0);
1927*433d6423SLionel Sambuc 	}
1928*433d6423SLionel Sambuc 
1929*433d6423SLionel Sambuc 	/* Tell the controller about some of the physical addresses. */
1930*433d6423SLionel Sambuc 	port_write(ps, AHCI_PORT_FBU, 0);
1931*433d6423SLionel Sambuc 	port_write(ps, AHCI_PORT_FB, ps->fis_phys);
1932*433d6423SLionel Sambuc 
1933*433d6423SLionel Sambuc 	port_write(ps, AHCI_PORT_CLBU, 0);
1934*433d6423SLionel Sambuc 	port_write(ps, AHCI_PORT_CLB, ps->cl_phys);
1935*433d6423SLionel Sambuc 
1936*433d6423SLionel Sambuc 	/* Enable FIS receive. */
1937*433d6423SLionel Sambuc 	cmd = port_read(ps, AHCI_PORT_CMD);
1938*433d6423SLionel Sambuc 	port_write(ps, AHCI_PORT_CMD, cmd | AHCI_PORT_CMD_FRE);
1939*433d6423SLionel Sambuc 
1940*433d6423SLionel Sambuc 	ps->pad_base = NULL;
1941*433d6423SLionel Sambuc 	ps->pad_size = 0;
1942*433d6423SLionel Sambuc }
1943*433d6423SLionel Sambuc 
1944*433d6423SLionel Sambuc /*===========================================================================*
1945*433d6423SLionel Sambuc  *				port_free				     *
1946*433d6423SLionel Sambuc  *===========================================================================*/
1947*433d6423SLionel Sambuc static void port_free(struct port_state *ps)
1948*433d6423SLionel Sambuc {
1949*433d6423SLionel Sambuc 	/* Disable FIS receipt for the given port, and free previously
1950*433d6423SLionel Sambuc 	 * allocated memory.
1951*433d6423SLionel Sambuc 	 */
1952*433d6423SLionel Sambuc 	u32_t cmd;
1953*433d6423SLionel Sambuc 	int i;
1954*433d6423SLionel Sambuc 
1955*433d6423SLionel Sambuc 	/* Disable FIS receive. */
1956*433d6423SLionel Sambuc 	cmd = port_read(ps, AHCI_PORT_CMD);
1957*433d6423SLionel Sambuc 
1958*433d6423SLionel Sambuc 	if (cmd & (AHCI_PORT_CMD_FR | AHCI_PORT_CMD_FRE)) {
1959*433d6423SLionel Sambuc 		port_write(ps, AHCI_PORT_CMD, cmd & ~AHCI_PORT_CMD_FRE);
1960*433d6423SLionel Sambuc 
1961*433d6423SLionel Sambuc 		SPIN_UNTIL(!(port_read(ps, AHCI_PORT_CMD) & AHCI_PORT_CMD_FR),
1962*433d6423SLionel Sambuc 			PORTREG_DELAY);
1963*433d6423SLionel Sambuc 	}
1964*433d6423SLionel Sambuc 
1965*433d6423SLionel Sambuc 	if (ps->pad_base != NULL)
1966*433d6423SLionel Sambuc 		free_contig(ps->pad_base, ps->pad_size);
1967*433d6423SLionel Sambuc 
1968*433d6423SLionel Sambuc 	/* The first command table is part of the primary memory page. */
1969*433d6423SLionel Sambuc 	for (i = 1; i < hba_state.nr_cmds; i++)
1970*433d6423SLionel Sambuc 		if (ps->ct_base[i] != NULL)
1971*433d6423SLionel Sambuc 			free_contig(ps->ct_base[i], AHCI_CT_SIZE);
1972*433d6423SLionel Sambuc 
1973*433d6423SLionel Sambuc 	free_contig(ps->mem_base, ps->mem_size);
1974*433d6423SLionel Sambuc }
1975*433d6423SLionel Sambuc 
1976*433d6423SLionel Sambuc /*===========================================================================*
1977*433d6423SLionel Sambuc  *				port_init				     *
1978*433d6423SLionel Sambuc  *===========================================================================*/
1979*433d6423SLionel Sambuc static void port_init(struct port_state *ps)
1980*433d6423SLionel Sambuc {
1981*433d6423SLionel Sambuc 	/* Initialize the given port.
1982*433d6423SLionel Sambuc 	 */
1983*433d6423SLionel Sambuc 	u32_t cmd;
1984*433d6423SLionel Sambuc 	int i;
1985*433d6423SLionel Sambuc 
1986*433d6423SLionel Sambuc 	/* Initialize the port state structure. */
1987*433d6423SLionel Sambuc 	ps->queue_depth = 1;
1988*433d6423SLionel Sambuc 	ps->state = STATE_SPIN_UP;
1989*433d6423SLionel Sambuc 	ps->flags = FLAG_BUSY;
1990*433d6423SLionel Sambuc 	ps->sector_size = 0;
1991*433d6423SLionel Sambuc 	ps->open_count = 0;
1992*433d6423SLionel Sambuc 	ps->pend_mask = 0;
1993*433d6423SLionel Sambuc 	for (i = 0; i < NR_CMDS; i++)
1994*433d6423SLionel Sambuc 		init_timer(&ps->cmd_info[i].timer);
1995*433d6423SLionel Sambuc 
1996*433d6423SLionel Sambuc 	ps->reg = (u32_t *) ((u8_t *) hba_state.base +
1997*433d6423SLionel Sambuc 		AHCI_MEM_BASE_SIZE + AHCI_MEM_PORT_SIZE * (ps - port_state));
1998*433d6423SLionel Sambuc 
1999*433d6423SLionel Sambuc 	/* Allocate memory for the port. */
2000*433d6423SLionel Sambuc 	port_alloc(ps);
2001*433d6423SLionel Sambuc 
2002*433d6423SLionel Sambuc 	/* Just listen for device connection events for now. */
2003*433d6423SLionel Sambuc 	port_write(ps, AHCI_PORT_IE, AHCI_PORT_IE_PCE);
2004*433d6423SLionel Sambuc 
2005*433d6423SLionel Sambuc 	/* Enable device spin-up for HBAs that support staggered spin-up.
2006*433d6423SLionel Sambuc 	 * This is a no-op for HBAs that do not support it.
2007*433d6423SLionel Sambuc 	 */
2008*433d6423SLionel Sambuc 	cmd = port_read(ps, AHCI_PORT_CMD);
2009*433d6423SLionel Sambuc 	port_write(ps, AHCI_PORT_CMD, cmd | AHCI_PORT_CMD_SUD);
2010*433d6423SLionel Sambuc 
2011*433d6423SLionel Sambuc 	/* Trigger a port reset. */
2012*433d6423SLionel Sambuc 	port_hardreset(ps);
2013*433d6423SLionel Sambuc 
2014*433d6423SLionel Sambuc 	set_timer(&ps->cmd_info[0].timer, ahci_spinup_timeout,
2015*433d6423SLionel Sambuc 		port_timeout, BUILD_ARG(ps - port_state, 0));
2016*433d6423SLionel Sambuc }
2017*433d6423SLionel Sambuc 
2018*433d6423SLionel Sambuc /*===========================================================================*
2019*433d6423SLionel Sambuc  *				ahci_probe				     *
2020*433d6423SLionel Sambuc  *===========================================================================*/
2021*433d6423SLionel Sambuc static int ahci_probe(int skip)
2022*433d6423SLionel Sambuc {
2023*433d6423SLionel Sambuc 	/* Find a matching PCI device.
2024*433d6423SLionel Sambuc 	 */
2025*433d6423SLionel Sambuc 	int r, devind;
2026*433d6423SLionel Sambuc 	u16_t vid, did;
2027*433d6423SLionel Sambuc 
2028*433d6423SLionel Sambuc 	pci_init();
2029*433d6423SLionel Sambuc 
2030*433d6423SLionel Sambuc 	r = pci_first_dev(&devind, &vid, &did);
2031*433d6423SLionel Sambuc 	if (r <= 0)
2032*433d6423SLionel Sambuc 		return -1;
2033*433d6423SLionel Sambuc 
2034*433d6423SLionel Sambuc 	while (skip--) {
2035*433d6423SLionel Sambuc 		r = pci_next_dev(&devind, &vid, &did);
2036*433d6423SLionel Sambuc 		if (r <= 0)
2037*433d6423SLionel Sambuc 			return -1;
2038*433d6423SLionel Sambuc 	}
2039*433d6423SLionel Sambuc 
2040*433d6423SLionel Sambuc 	pci_reserve(devind);
2041*433d6423SLionel Sambuc 
2042*433d6423SLionel Sambuc 	return devind;
2043*433d6423SLionel Sambuc }
2044*433d6423SLionel Sambuc 
2045*433d6423SLionel Sambuc /*===========================================================================*
2046*433d6423SLionel Sambuc  *				ahci_reset				     *
2047*433d6423SLionel Sambuc  *===========================================================================*/
2048*433d6423SLionel Sambuc static void ahci_reset(void)
2049*433d6423SLionel Sambuc {
2050*433d6423SLionel Sambuc 	/* Reset the HBA. Do not enable AHCI mode afterwards.
2051*433d6423SLionel Sambuc 	 */
2052*433d6423SLionel Sambuc 	u32_t ghc;
2053*433d6423SLionel Sambuc 
2054*433d6423SLionel Sambuc 	ghc = hba_read(AHCI_HBA_GHC);
2055*433d6423SLionel Sambuc 
2056*433d6423SLionel Sambuc 	hba_write(AHCI_HBA_GHC, ghc | AHCI_HBA_GHC_AE);
2057*433d6423SLionel Sambuc 
2058*433d6423SLionel Sambuc 	hba_write(AHCI_HBA_GHC, ghc | AHCI_HBA_GHC_AE | AHCI_HBA_GHC_HR);
2059*433d6423SLionel Sambuc 
2060*433d6423SLionel Sambuc 	SPIN_UNTIL(!(hba_read(AHCI_HBA_GHC) & AHCI_HBA_GHC_HR), RESET_DELAY);
2061*433d6423SLionel Sambuc 
2062*433d6423SLionel Sambuc 	if (hba_read(AHCI_HBA_GHC) & AHCI_HBA_GHC_HR)
2063*433d6423SLionel Sambuc 		panic("unable to reset HBA");
2064*433d6423SLionel Sambuc }
2065*433d6423SLionel Sambuc 
2066*433d6423SLionel Sambuc /*===========================================================================*
2067*433d6423SLionel Sambuc  *				ahci_init				     *
2068*433d6423SLionel Sambuc  *===========================================================================*/
2069*433d6423SLionel Sambuc static void ahci_init(int devind)
2070*433d6423SLionel Sambuc {
2071*433d6423SLionel Sambuc 	/* Initialize the device.
2072*433d6423SLionel Sambuc 	 */
2073*433d6423SLionel Sambuc 	u32_t base, size, cap, ghc, mask;
2074*433d6423SLionel Sambuc 	int r, port, ioflag;
2075*433d6423SLionel Sambuc 
2076*433d6423SLionel Sambuc 	if ((r = pci_get_bar(devind, PCI_BAR_6, &base, &size, &ioflag)) != OK)
2077*433d6423SLionel Sambuc 		panic("unable to retrieve BAR: %d", r);
2078*433d6423SLionel Sambuc 
2079*433d6423SLionel Sambuc 	if (ioflag)
2080*433d6423SLionel Sambuc 		panic("invalid BAR type");
2081*433d6423SLionel Sambuc 
2082*433d6423SLionel Sambuc 	/* There must be at least one port, and at most NR_PORTS ports. Limit
2083*433d6423SLionel Sambuc 	 * the actual total number of ports to the size of the exposed area.
2084*433d6423SLionel Sambuc 	 */
2085*433d6423SLionel Sambuc 	if (size < AHCI_MEM_BASE_SIZE + AHCI_MEM_PORT_SIZE)
2086*433d6423SLionel Sambuc 		panic("HBA memory size too small: %u", size);
2087*433d6423SLionel Sambuc 
2088*433d6423SLionel Sambuc 	size = MIN(size, AHCI_MEM_BASE_SIZE + AHCI_MEM_PORT_SIZE * NR_PORTS);
2089*433d6423SLionel Sambuc 
2090*433d6423SLionel Sambuc 	hba_state.nr_ports = (size - AHCI_MEM_BASE_SIZE) / AHCI_MEM_PORT_SIZE;
2091*433d6423SLionel Sambuc 
2092*433d6423SLionel Sambuc 	/* Map the register area into local memory. */
2093*433d6423SLionel Sambuc 	hba_state.base = (u32_t *) vm_map_phys(SELF, (void *) base, size);
2094*433d6423SLionel Sambuc 	hba_state.size = size;
2095*433d6423SLionel Sambuc 	if (hba_state.base == MAP_FAILED)
2096*433d6423SLionel Sambuc 		panic("unable to map HBA memory");
2097*433d6423SLionel Sambuc 
2098*433d6423SLionel Sambuc 	/* Retrieve, allocate and enable the controller's IRQ. */
2099*433d6423SLionel Sambuc 	hba_state.irq = pci_attr_r8(devind, PCI_ILR);
2100*433d6423SLionel Sambuc 	hba_state.hook_id = 0;
2101*433d6423SLionel Sambuc 
2102*433d6423SLionel Sambuc 	if ((r = sys_irqsetpolicy(hba_state.irq, 0, &hba_state.hook_id)) != OK)
2103*433d6423SLionel Sambuc 		panic("unable to register IRQ: %d", r);
2104*433d6423SLionel Sambuc 
2105*433d6423SLionel Sambuc 	if ((r = sys_irqenable(&hba_state.hook_id)) != OK)
2106*433d6423SLionel Sambuc 		panic("unable to enable IRQ: %d", r);
2107*433d6423SLionel Sambuc 
2108*433d6423SLionel Sambuc 	/* Reset the HBA. */
2109*433d6423SLionel Sambuc 	ahci_reset();
2110*433d6423SLionel Sambuc 
2111*433d6423SLionel Sambuc 	/* Enable AHCI and interrupts. */
2112*433d6423SLionel Sambuc 	ghc = hba_read(AHCI_HBA_GHC);
2113*433d6423SLionel Sambuc 	hba_write(AHCI_HBA_GHC, ghc | AHCI_HBA_GHC_AE | AHCI_HBA_GHC_IE);
2114*433d6423SLionel Sambuc 
2115*433d6423SLionel Sambuc 	/* Limit the maximum number of commands to the controller's value. */
2116*433d6423SLionel Sambuc 	/* Note that we currently use only one command anyway. */
2117*433d6423SLionel Sambuc 	cap = hba_read(AHCI_HBA_CAP);
2118*433d6423SLionel Sambuc 	hba_state.has_ncq = !!(cap & AHCI_HBA_CAP_SNCQ);
2119*433d6423SLionel Sambuc 	hba_state.has_clo = !!(cap & AHCI_HBA_CAP_SCLO);
2120*433d6423SLionel Sambuc 	hba_state.nr_cmds = MIN(NR_CMDS,
2121*433d6423SLionel Sambuc 		((cap >> AHCI_HBA_CAP_NCS_SHIFT) & AHCI_HBA_CAP_NCS_MASK) + 1);
2122*433d6423SLionel Sambuc 
2123*433d6423SLionel Sambuc 	dprintf(V_INFO, ("AHCI%u: HBA v%d.%d%d, %ld ports, %ld commands, "
2124*433d6423SLionel Sambuc 		"%s queuing, IRQ %d\n",
2125*433d6423SLionel Sambuc 		ahci_instance,
2126*433d6423SLionel Sambuc 		(int) (hba_read(AHCI_HBA_VS) >> 16),
2127*433d6423SLionel Sambuc 		(int) ((hba_read(AHCI_HBA_VS) >> 8) & 0xFF),
2128*433d6423SLionel Sambuc 		(int) (hba_read(AHCI_HBA_VS) & 0xFF),
2129*433d6423SLionel Sambuc 		((cap >> AHCI_HBA_CAP_NP_SHIFT) & AHCI_HBA_CAP_NP_MASK) + 1,
2130*433d6423SLionel Sambuc 		((cap >> AHCI_HBA_CAP_NCS_SHIFT) & AHCI_HBA_CAP_NCS_MASK) + 1,
2131*433d6423SLionel Sambuc 		hba_state.has_ncq ? "supports" : "no", hba_state.irq));
2132*433d6423SLionel Sambuc 
2133*433d6423SLionel Sambuc 	dprintf(V_INFO, ("AHCI%u: CAP %08x, CAP2 %08x, PI %08x\n",
2134*433d6423SLionel Sambuc 		ahci_instance, cap, hba_read(AHCI_HBA_CAP2),
2135*433d6423SLionel Sambuc 		hba_read(AHCI_HBA_PI)));
2136*433d6423SLionel Sambuc 
2137*433d6423SLionel Sambuc 	/* Initialize each of the implemented ports. We ignore CAP.NP. */
2138*433d6423SLionel Sambuc 	mask = hba_read(AHCI_HBA_PI);
2139*433d6423SLionel Sambuc 
2140*433d6423SLionel Sambuc 	for (port = 0; port < hba_state.nr_ports; port++) {
2141*433d6423SLionel Sambuc 		port_state[port].device = NO_DEVICE;
2142*433d6423SLionel Sambuc 		port_state[port].state = STATE_NO_PORT;
2143*433d6423SLionel Sambuc 
2144*433d6423SLionel Sambuc 		if (mask & (1 << port))
2145*433d6423SLionel Sambuc 			port_init(&port_state[port]);
2146*433d6423SLionel Sambuc 	}
2147*433d6423SLionel Sambuc }
2148*433d6423SLionel Sambuc 
2149*433d6423SLionel Sambuc /*===========================================================================*
2150*433d6423SLionel Sambuc  *				ahci_stop				     *
2151*433d6423SLionel Sambuc  *===========================================================================*/
2152*433d6423SLionel Sambuc static void ahci_stop(void)
2153*433d6423SLionel Sambuc {
2154*433d6423SLionel Sambuc 	/* Disable AHCI, and clean up resources to the extent possible.
2155*433d6423SLionel Sambuc 	 */
2156*433d6423SLionel Sambuc 	struct port_state *ps;
2157*433d6423SLionel Sambuc 	int r, port;
2158*433d6423SLionel Sambuc 
2159*433d6423SLionel Sambuc 	for (port = 0; port < hba_state.nr_ports; port++) {
2160*433d6423SLionel Sambuc 		ps = &port_state[port];
2161*433d6423SLionel Sambuc 
2162*433d6423SLionel Sambuc 		if (ps->state != STATE_NO_PORT) {
2163*433d6423SLionel Sambuc 			port_stop(ps);
2164*433d6423SLionel Sambuc 
2165*433d6423SLionel Sambuc 			port_free(ps);
2166*433d6423SLionel Sambuc 		}
2167*433d6423SLionel Sambuc 	}
2168*433d6423SLionel Sambuc 
2169*433d6423SLionel Sambuc 	ahci_reset();
2170*433d6423SLionel Sambuc 
2171*433d6423SLionel Sambuc 	if ((r = vm_unmap_phys(SELF, (void *) hba_state.base,
2172*433d6423SLionel Sambuc 			hba_state.size)) != OK)
2173*433d6423SLionel Sambuc 		panic("unable to unmap HBA memory: %d", r);
2174*433d6423SLionel Sambuc 
2175*433d6423SLionel Sambuc 	if ((r = sys_irqrmpolicy(&hba_state.hook_id)) != OK)
2176*433d6423SLionel Sambuc 		panic("unable to deregister IRQ: %d", r);
2177*433d6423SLionel Sambuc }
2178*433d6423SLionel Sambuc 
2179*433d6423SLionel Sambuc /*===========================================================================*
2180*433d6423SLionel Sambuc  *				ahci_alarm				     *
2181*433d6423SLionel Sambuc  *===========================================================================*/
2182*433d6423SLionel Sambuc static void ahci_alarm(clock_t stamp)
2183*433d6423SLionel Sambuc {
2184*433d6423SLionel Sambuc 	/* Process an alarm.
2185*433d6423SLionel Sambuc 	 */
2186*433d6423SLionel Sambuc 
2187*433d6423SLionel Sambuc 	/* Call the port-specific handler for each port that timed out. */
2188*433d6423SLionel Sambuc 	expire_timers(stamp);
2189*433d6423SLionel Sambuc }
2190*433d6423SLionel Sambuc 
2191*433d6423SLionel Sambuc /*===========================================================================*
2192*433d6423SLionel Sambuc  *				ahci_intr				     *
2193*433d6423SLionel Sambuc  *===========================================================================*/
2194*433d6423SLionel Sambuc static void ahci_intr(unsigned int UNUSED(mask))
2195*433d6423SLionel Sambuc {
2196*433d6423SLionel Sambuc 	/* Process an interrupt.
2197*433d6423SLionel Sambuc 	 */
2198*433d6423SLionel Sambuc 	struct port_state *ps;
2199*433d6423SLionel Sambuc 	u32_t mask;
2200*433d6423SLionel Sambuc 	int r, port;
2201*433d6423SLionel Sambuc 
2202*433d6423SLionel Sambuc 	/* Handle an interrupt for each port that has the interrupt bit set. */
2203*433d6423SLionel Sambuc 	mask = hba_read(AHCI_HBA_IS);
2204*433d6423SLionel Sambuc 
2205*433d6423SLionel Sambuc 	for (port = 0; port < hba_state.nr_ports; port++) {
2206*433d6423SLionel Sambuc 		if (mask & (1 << port)) {
2207*433d6423SLionel Sambuc 			ps = &port_state[port];
2208*433d6423SLionel Sambuc 
2209*433d6423SLionel Sambuc 			port_intr(ps);
2210*433d6423SLionel Sambuc 
2211*433d6423SLionel Sambuc 			/* After processing an interrupt, wake up the device
2212*433d6423SLionel Sambuc 			 * thread if it is suspended and now no longer busy.
2213*433d6423SLionel Sambuc 			 */
2214*433d6423SLionel Sambuc 			if ((ps->flags & (FLAG_SUSPENDED | FLAG_BUSY)) ==
2215*433d6423SLionel Sambuc 					FLAG_SUSPENDED)
2216*433d6423SLionel Sambuc 				blockdriver_mt_wakeup(ps->cmd_info[0].tid);
2217*433d6423SLionel Sambuc 		}
2218*433d6423SLionel Sambuc 	}
2219*433d6423SLionel Sambuc 
2220*433d6423SLionel Sambuc 	/* Clear the bits that we processed. */
2221*433d6423SLionel Sambuc 	hba_write(AHCI_HBA_IS, mask);
2222*433d6423SLionel Sambuc 
2223*433d6423SLionel Sambuc 	/* Reenable the interrupt. */
2224*433d6423SLionel Sambuc 	if ((r = sys_irqenable(&hba_state.hook_id)) != OK)
2225*433d6423SLionel Sambuc 		panic("unable to enable IRQ: %d", r);
2226*433d6423SLionel Sambuc }
2227*433d6423SLionel Sambuc 
2228*433d6423SLionel Sambuc /*===========================================================================*
2229*433d6423SLionel Sambuc  *				ahci_get_params				     *
2230*433d6423SLionel Sambuc  *===========================================================================*/
2231*433d6423SLionel Sambuc static void ahci_get_params(void)
2232*433d6423SLionel Sambuc {
2233*433d6423SLionel Sambuc 	/* Retrieve and parse parameters passed to this driver, except the
2234*433d6423SLionel Sambuc 	 * device-to-port mapping, which has to be parsed later.
2235*433d6423SLionel Sambuc 	 */
2236*433d6423SLionel Sambuc 	long v;
2237*433d6423SLionel Sambuc 	unsigned int i;
2238*433d6423SLionel Sambuc 
2239*433d6423SLionel Sambuc 	/* Find out which driver instance we are. */
2240*433d6423SLionel Sambuc 	v = 0;
2241*433d6423SLionel Sambuc 	(void) env_parse("instance", "d", 0, &v, 0, 255);
2242*433d6423SLionel Sambuc 	ahci_instance = (int) v;
2243*433d6423SLionel Sambuc 
2244*433d6423SLionel Sambuc 	/* Initialize the verbosity level. */
2245*433d6423SLionel Sambuc 	v = V_ERR;
2246*433d6423SLionel Sambuc 	(void) env_parse("ahci_verbose", "d", 0, &v, V_NONE, V_REQ);
2247*433d6423SLionel Sambuc 	ahci_verbose = (int) v;
2248*433d6423SLionel Sambuc 
2249*433d6423SLionel Sambuc 	/* Initialize timeout-related values. */
2250*433d6423SLionel Sambuc 	for (i = 0; i < sizeof(ahci_timevar) / sizeof(ahci_timevar[0]); i++) {
2251*433d6423SLionel Sambuc 		v = ahci_timevar[i].default_ms;
2252*433d6423SLionel Sambuc 
2253*433d6423SLionel Sambuc 		(void) env_parse(ahci_timevar[i].name, "d", 0, &v, 1,
2254*433d6423SLionel Sambuc 			LONG_MAX);
2255*433d6423SLionel Sambuc 
2256*433d6423SLionel Sambuc 		*ahci_timevar[i].ptr = millis_to_hz(v);
2257*433d6423SLionel Sambuc 	}
2258*433d6423SLionel Sambuc 
2259*433d6423SLionel Sambuc 	ahci_device_delay = millis_to_hz(DEVICE_DELAY);
2260*433d6423SLionel Sambuc 	ahci_device_checks = (ahci_device_timeout + ahci_device_delay - 1) /
2261*433d6423SLionel Sambuc 		ahci_device_delay;
2262*433d6423SLionel Sambuc }
2263*433d6423SLionel Sambuc 
2264*433d6423SLionel Sambuc /*===========================================================================*
2265*433d6423SLionel Sambuc  *				ahci_set_mapping			     *
2266*433d6423SLionel Sambuc  *===========================================================================*/
2267*433d6423SLionel Sambuc static void ahci_set_mapping(void)
2268*433d6423SLionel Sambuc {
2269*433d6423SLionel Sambuc 	/* Construct a mapping from device nodes to port numbers.
2270*433d6423SLionel Sambuc 	 */
2271*433d6423SLionel Sambuc 	char key[16], val[32], *p;
2272*433d6423SLionel Sambuc 	unsigned int port;
2273*433d6423SLionel Sambuc 	int i, j;
2274*433d6423SLionel Sambuc 
2275*433d6423SLionel Sambuc 	/* Start off with a mapping that includes implemented ports only, in
2276*433d6423SLionel Sambuc 	 * order. We choose this mapping over an identity mapping to maximize
2277*433d6423SLionel Sambuc 	 * the chance that the user will be able to access the first MAX_DRIVES
2278*433d6423SLionel Sambuc 	 * devices. Note that we can only do this after initializing the HBA.
2279*433d6423SLionel Sambuc 	 */
2280*433d6423SLionel Sambuc 	for (i = j = 0; i < NR_PORTS && j < MAX_DRIVES; i++)
2281*433d6423SLionel Sambuc 		if (port_state[i].state != STATE_NO_PORT)
2282*433d6423SLionel Sambuc 			ahci_map[j++] = i;
2283*433d6423SLionel Sambuc 
2284*433d6423SLionel Sambuc 	for ( ; j < MAX_DRIVES; j++)
2285*433d6423SLionel Sambuc 		ahci_map[j] = NO_PORT;
2286*433d6423SLionel Sambuc 
2287*433d6423SLionel Sambuc 	/* See if the user specified a custom mapping. Unlike all other
2288*433d6423SLionel Sambuc 	 * configuration options, this is a per-instance setting.
2289*433d6423SLionel Sambuc 	 */
2290*433d6423SLionel Sambuc 	strlcpy(key, "ahci0_map", sizeof(key));
2291*433d6423SLionel Sambuc 	key[4] += ahci_instance;
2292*433d6423SLionel Sambuc 
2293*433d6423SLionel Sambuc 	if (env_get_param(key, val, sizeof(val)) == OK) {
2294*433d6423SLionel Sambuc 		/* Parse the mapping, which is assumed to be a comma-separated
2295*433d6423SLionel Sambuc 		 * list of zero-based port numbers.
2296*433d6423SLionel Sambuc 		 */
2297*433d6423SLionel Sambuc 		p = val;
2298*433d6423SLionel Sambuc 
2299*433d6423SLionel Sambuc 		for (i = 0; i < MAX_DRIVES; i++) {
2300*433d6423SLionel Sambuc 			if (*p) {
2301*433d6423SLionel Sambuc 				port = (unsigned int) strtoul(p, &p, 0);
2302*433d6423SLionel Sambuc 
2303*433d6423SLionel Sambuc 				if (*p) p++;
2304*433d6423SLionel Sambuc 
2305*433d6423SLionel Sambuc 				ahci_map[i] = port % NR_PORTS;
2306*433d6423SLionel Sambuc 			}
2307*433d6423SLionel Sambuc 			else ahci_map[i] = NO_PORT;
2308*433d6423SLionel Sambuc 		}
2309*433d6423SLionel Sambuc 	}
2310*433d6423SLionel Sambuc 
2311*433d6423SLionel Sambuc 	/* Create a reverse mapping. */
2312*433d6423SLionel Sambuc 	for (i = 0; i < MAX_DRIVES; i++)
2313*433d6423SLionel Sambuc 		if ((j = ahci_map[i]) != NO_PORT)
2314*433d6423SLionel Sambuc 			port_state[j].device = i;
2315*433d6423SLionel Sambuc }
2316*433d6423SLionel Sambuc 
2317*433d6423SLionel Sambuc /*===========================================================================*
2318*433d6423SLionel Sambuc  *				sef_cb_init_fresh			     *
2319*433d6423SLionel Sambuc  *===========================================================================*/
2320*433d6423SLionel Sambuc static int sef_cb_init_fresh(int type, sef_init_info_t *UNUSED(info))
2321*433d6423SLionel Sambuc {
2322*433d6423SLionel Sambuc 	/* Initialize the driver.
2323*433d6423SLionel Sambuc 	 */
2324*433d6423SLionel Sambuc 	int devind;
2325*433d6423SLionel Sambuc 
2326*433d6423SLionel Sambuc 	/* Get command line parameters. */
2327*433d6423SLionel Sambuc 	ahci_get_params();
2328*433d6423SLionel Sambuc 
2329*433d6423SLionel Sambuc 	/* Probe for recognized devices, skipping matches as appropriate. */
2330*433d6423SLionel Sambuc 	devind = ahci_probe(ahci_instance);
2331*433d6423SLionel Sambuc 
2332*433d6423SLionel Sambuc 	if (devind < 0)
2333*433d6423SLionel Sambuc 		panic("no matching device found");
2334*433d6423SLionel Sambuc 
2335*433d6423SLionel Sambuc 	/* Initialize the device we found. */
2336*433d6423SLionel Sambuc 	ahci_init(devind);
2337*433d6423SLionel Sambuc 
2338*433d6423SLionel Sambuc 	/* Create a mapping from device nodes to port numbers. */
2339*433d6423SLionel Sambuc 	ahci_set_mapping();
2340*433d6423SLionel Sambuc 
2341*433d6423SLionel Sambuc 	/* Announce that we are up. */
2342*433d6423SLionel Sambuc 	blockdriver_announce(type);
2343*433d6423SLionel Sambuc 
2344*433d6423SLionel Sambuc 	return OK;
2345*433d6423SLionel Sambuc }
2346*433d6423SLionel Sambuc 
2347*433d6423SLionel Sambuc /*===========================================================================*
2348*433d6423SLionel Sambuc  *				sef_cb_signal_handler			     *
2349*433d6423SLionel Sambuc  *===========================================================================*/
2350*433d6423SLionel Sambuc static void sef_cb_signal_handler(int signo)
2351*433d6423SLionel Sambuc {
2352*433d6423SLionel Sambuc 	/* In case of a termination signal, shut down this driver.
2353*433d6423SLionel Sambuc 	 */
2354*433d6423SLionel Sambuc 	int port;
2355*433d6423SLionel Sambuc 
2356*433d6423SLionel Sambuc 	if (signo != SIGTERM) return;
2357*433d6423SLionel Sambuc 
2358*433d6423SLionel Sambuc 	/* If any ports are still opened, assume that the system is being shut
2359*433d6423SLionel Sambuc 	 * down, and stay up until the last device has been closed.
2360*433d6423SLionel Sambuc 	 */
2361*433d6423SLionel Sambuc 	ahci_exiting = TRUE;
2362*433d6423SLionel Sambuc 
2363*433d6423SLionel Sambuc 	for (port = 0; port < hba_state.nr_ports; port++)
2364*433d6423SLionel Sambuc 		if (port_state[port].open_count > 0)
2365*433d6423SLionel Sambuc 			return;
2366*433d6423SLionel Sambuc 
2367*433d6423SLionel Sambuc 	/* If not, stop the driver and exit immediately. */
2368*433d6423SLionel Sambuc 	ahci_stop();
2369*433d6423SLionel Sambuc 
2370*433d6423SLionel Sambuc 	exit(0);
2371*433d6423SLionel Sambuc }
2372*433d6423SLionel Sambuc 
2373*433d6423SLionel Sambuc /*===========================================================================*
2374*433d6423SLionel Sambuc  *				sef_local_startup			     *
2375*433d6423SLionel Sambuc  *===========================================================================*/
2376*433d6423SLionel Sambuc static void sef_local_startup(void)
2377*433d6423SLionel Sambuc {
2378*433d6423SLionel Sambuc 	/* Set callbacks and initialize the System Event Framework (SEF).
2379*433d6423SLionel Sambuc 	 */
2380*433d6423SLionel Sambuc 
2381*433d6423SLionel Sambuc 	/* Register init callbacks. */
2382*433d6423SLionel Sambuc 	sef_setcb_init_fresh(sef_cb_init_fresh);
2383*433d6423SLionel Sambuc 	sef_setcb_init_lu(sef_cb_init_fresh);
2384*433d6423SLionel Sambuc 
2385*433d6423SLionel Sambuc 	/* Register signal callbacks. */
2386*433d6423SLionel Sambuc 	sef_setcb_signal_handler(sef_cb_signal_handler);
2387*433d6423SLionel Sambuc 
2388*433d6423SLionel Sambuc 	/* Let SEF perform startup. */
2389*433d6423SLionel Sambuc 	sef_startup();
2390*433d6423SLionel Sambuc }
2391*433d6423SLionel Sambuc 
2392*433d6423SLionel Sambuc /*===========================================================================*
2393*433d6423SLionel Sambuc  *				ahci_portname				     *
2394*433d6423SLionel Sambuc  *===========================================================================*/
2395*433d6423SLionel Sambuc static char *ahci_portname(struct port_state *ps)
2396*433d6423SLionel Sambuc {
2397*433d6423SLionel Sambuc 	/* Return a printable name for the given port. Whenever we can, print a
2398*433d6423SLionel Sambuc 	 * "Dx" device number rather than a "Pxx" port number, because the user
2399*433d6423SLionel Sambuc 	 * may not be aware of the mapping currently in use.
2400*433d6423SLionel Sambuc 	 */
2401*433d6423SLionel Sambuc 	static char name[] = "AHCI0-P00";
2402*433d6423SLionel Sambuc 
2403*433d6423SLionel Sambuc 	name[4] = '0' + ahci_instance;
2404*433d6423SLionel Sambuc 
2405*433d6423SLionel Sambuc 	if (ps->device == NO_DEVICE) {
2406*433d6423SLionel Sambuc 		name[6] = 'P';
2407*433d6423SLionel Sambuc 		name[7] = '0' + (ps - port_state) / 10;
2408*433d6423SLionel Sambuc 		name[8] = '0' + (ps - port_state) % 10;
2409*433d6423SLionel Sambuc 	}
2410*433d6423SLionel Sambuc 	else {
2411*433d6423SLionel Sambuc 		name[6] = 'D';
2412*433d6423SLionel Sambuc 		name[7] = '0' + ps->device;
2413*433d6423SLionel Sambuc 		name[8] = 0;
2414*433d6423SLionel Sambuc 	}
2415*433d6423SLionel Sambuc 
2416*433d6423SLionel Sambuc 	return name;
2417*433d6423SLionel Sambuc }
2418*433d6423SLionel Sambuc 
2419*433d6423SLionel Sambuc /*===========================================================================*
2420*433d6423SLionel Sambuc  *				ahci_map_minor				     *
2421*433d6423SLionel Sambuc  *===========================================================================*/
2422*433d6423SLionel Sambuc static struct port_state *ahci_map_minor(devminor_t minor, struct device **dvp)
2423*433d6423SLionel Sambuc {
2424*433d6423SLionel Sambuc 	/* Map a minor device number to a port and a pointer to the partition's
2425*433d6423SLionel Sambuc 	 * device structure. Return NULL if this minor device number does not
2426*433d6423SLionel Sambuc 	 * identify an actual device.
2427*433d6423SLionel Sambuc 	 */
2428*433d6423SLionel Sambuc 	struct port_state *ps;
2429*433d6423SLionel Sambuc 	int port;
2430*433d6423SLionel Sambuc 
2431*433d6423SLionel Sambuc 	ps = NULL;
2432*433d6423SLionel Sambuc 
2433*433d6423SLionel Sambuc 	if (minor >= 0 && minor < NR_MINORS) {
2434*433d6423SLionel Sambuc 		port = ahci_map[minor / DEV_PER_DRIVE];
2435*433d6423SLionel Sambuc 
2436*433d6423SLionel Sambuc 		if (port == NO_PORT)
2437*433d6423SLionel Sambuc 			return NULL;
2438*433d6423SLionel Sambuc 
2439*433d6423SLionel Sambuc 		ps = &port_state[port];
2440*433d6423SLionel Sambuc 		*dvp = &ps->part[minor % DEV_PER_DRIVE];
2441*433d6423SLionel Sambuc 	}
2442*433d6423SLionel Sambuc 	else if ((unsigned) (minor -= MINOR_d0p0s0) < NR_SUBDEVS) {
2443*433d6423SLionel Sambuc 		port = ahci_map[minor / SUB_PER_DRIVE];
2444*433d6423SLionel Sambuc 
2445*433d6423SLionel Sambuc 		if (port == NO_PORT)
2446*433d6423SLionel Sambuc 			return NULL;
2447*433d6423SLionel Sambuc 
2448*433d6423SLionel Sambuc 		ps = &port_state[port];
2449*433d6423SLionel Sambuc 		*dvp = &ps->subpart[minor % SUB_PER_DRIVE];
2450*433d6423SLionel Sambuc 	}
2451*433d6423SLionel Sambuc 
2452*433d6423SLionel Sambuc 	return ps;
2453*433d6423SLionel Sambuc }
2454*433d6423SLionel Sambuc 
2455*433d6423SLionel Sambuc /*===========================================================================*
2456*433d6423SLionel Sambuc  *				ahci_part				     *
2457*433d6423SLionel Sambuc  *===========================================================================*/
2458*433d6423SLionel Sambuc static struct device *ahci_part(devminor_t minor)
2459*433d6423SLionel Sambuc {
2460*433d6423SLionel Sambuc 	/* Return a pointer to the partition information structure of the given
2461*433d6423SLionel Sambuc 	 * minor device.
2462*433d6423SLionel Sambuc 	 */
2463*433d6423SLionel Sambuc 	struct device *dv;
2464*433d6423SLionel Sambuc 
2465*433d6423SLionel Sambuc 	if (ahci_map_minor(minor, &dv) == NULL)
2466*433d6423SLionel Sambuc 		return NULL;
2467*433d6423SLionel Sambuc 
2468*433d6423SLionel Sambuc 	return dv;
2469*433d6423SLionel Sambuc }
2470*433d6423SLionel Sambuc 
2471*433d6423SLionel Sambuc /*===========================================================================*
2472*433d6423SLionel Sambuc  *				ahci_open				     *
2473*433d6423SLionel Sambuc  *===========================================================================*/
2474*433d6423SLionel Sambuc static int ahci_open(devminor_t minor, int access)
2475*433d6423SLionel Sambuc {
2476*433d6423SLionel Sambuc 	/* Open a device.
2477*433d6423SLionel Sambuc 	 */
2478*433d6423SLionel Sambuc 	struct port_state *ps;
2479*433d6423SLionel Sambuc 	int r;
2480*433d6423SLionel Sambuc 
2481*433d6423SLionel Sambuc 	ps = ahci_get_port(minor);
2482*433d6423SLionel Sambuc 
2483*433d6423SLionel Sambuc 	/* Only one open request can be processed at a time, due to the fact
2484*433d6423SLionel Sambuc 	 * that it is an exclusive operation. The thread that handles this call
2485*433d6423SLionel Sambuc 	 * can therefore freely register itself at slot zero.
2486*433d6423SLionel Sambuc 	 */
2487*433d6423SLionel Sambuc 	ps->cmd_info[0].tid = blockdriver_mt_get_tid();
2488*433d6423SLionel Sambuc 
2489*433d6423SLionel Sambuc 	/* If we are still in the process of initializing this port or device,
2490*433d6423SLionel Sambuc 	 * wait for completion of that phase first.
2491*433d6423SLionel Sambuc 	 */
2492*433d6423SLionel Sambuc 	if (ps->flags & FLAG_BUSY)
2493*433d6423SLionel Sambuc 		port_wait(ps);
2494*433d6423SLionel Sambuc 
2495*433d6423SLionel Sambuc 	/* The device may only be opened if it is now properly functioning. */
2496*433d6423SLionel Sambuc 	if (ps->state != STATE_GOOD_DEV)
2497*433d6423SLionel Sambuc 		return ENXIO;
2498*433d6423SLionel Sambuc 
2499*433d6423SLionel Sambuc 	/* Some devices may only be opened in read-only mode. */
2500*433d6423SLionel Sambuc 	if ((ps->flags & FLAG_READONLY) && (access & BDEV_W_BIT))
2501*433d6423SLionel Sambuc 		return EACCES;
2502*433d6423SLionel Sambuc 
2503*433d6423SLionel Sambuc 	if (ps->open_count == 0) {
2504*433d6423SLionel Sambuc 		/* The first open request. Clear the barrier flag, if set. */
2505*433d6423SLionel Sambuc 		ps->flags &= ~FLAG_BARRIER;
2506*433d6423SLionel Sambuc 
2507*433d6423SLionel Sambuc 		/* Recheck media only when nobody is using the device. */
2508*433d6423SLionel Sambuc 		if ((ps->flags & FLAG_ATAPI) &&
2509*433d6423SLionel Sambuc 			(r = atapi_check_medium(ps, 0)) != OK)
2510*433d6423SLionel Sambuc 			return r;
2511*433d6423SLionel Sambuc 
2512*433d6423SLionel Sambuc 		/* After rechecking the media, the partition table must always
2513*433d6423SLionel Sambuc 		 * be read. This is also a convenient time to do it for
2514*433d6423SLionel Sambuc 		 * nonremovable devices. Start by resetting the partition
2515*433d6423SLionel Sambuc 		 * tables and setting the working size of the entire device.
2516*433d6423SLionel Sambuc 		 */
2517*433d6423SLionel Sambuc 		memset(ps->part, 0, sizeof(ps->part));
2518*433d6423SLionel Sambuc 		memset(ps->subpart, 0, sizeof(ps->subpart));
2519*433d6423SLionel Sambuc 
2520*433d6423SLionel Sambuc 		ps->part[0].dv_size = ps->lba_count * ps->sector_size;
2521*433d6423SLionel Sambuc 
2522*433d6423SLionel Sambuc 		partition(&ahci_dtab, ps->device * DEV_PER_DRIVE, P_PRIMARY,
2523*433d6423SLionel Sambuc 			!!(ps->flags & FLAG_ATAPI));
2524*433d6423SLionel Sambuc 
2525*433d6423SLionel Sambuc 		blockdriver_mt_set_workers(ps->device, ps->queue_depth);
2526*433d6423SLionel Sambuc 	}
2527*433d6423SLionel Sambuc 	else {
2528*433d6423SLionel Sambuc 		/* If the barrier flag is set, deny new open requests until the
2529*433d6423SLionel Sambuc 		 * device is fully closed first.
2530*433d6423SLionel Sambuc 		 */
2531*433d6423SLionel Sambuc 		if (ps->flags & FLAG_BARRIER)
2532*433d6423SLionel Sambuc 			return ENXIO;
2533*433d6423SLionel Sambuc 	}
2534*433d6423SLionel Sambuc 
2535*433d6423SLionel Sambuc 	ps->open_count++;
2536*433d6423SLionel Sambuc 
2537*433d6423SLionel Sambuc 	return OK;
2538*433d6423SLionel Sambuc }
2539*433d6423SLionel Sambuc 
2540*433d6423SLionel Sambuc /*===========================================================================*
2541*433d6423SLionel Sambuc  *				ahci_close				     *
2542*433d6423SLionel Sambuc  *===========================================================================*/
2543*433d6423SLionel Sambuc static int ahci_close(devminor_t minor)
2544*433d6423SLionel Sambuc {
2545*433d6423SLionel Sambuc 	/* Close a device.
2546*433d6423SLionel Sambuc 	 */
2547*433d6423SLionel Sambuc 	struct port_state *ps;
2548*433d6423SLionel Sambuc 	int port;
2549*433d6423SLionel Sambuc 
2550*433d6423SLionel Sambuc 	ps = ahci_get_port(minor);
2551*433d6423SLionel Sambuc 
2552*433d6423SLionel Sambuc 	/* Decrease the open count. */
2553*433d6423SLionel Sambuc 	if (ps->open_count <= 0) {
2554*433d6423SLionel Sambuc 		dprintf(V_ERR, ("%s: closing already-closed port\n",
2555*433d6423SLionel Sambuc 			ahci_portname(ps)));
2556*433d6423SLionel Sambuc 
2557*433d6423SLionel Sambuc 		return EINVAL;
2558*433d6423SLionel Sambuc 	}
2559*433d6423SLionel Sambuc 
2560*433d6423SLionel Sambuc 	ps->open_count--;
2561*433d6423SLionel Sambuc 
2562*433d6423SLionel Sambuc 	if (ps->open_count > 0)
2563*433d6423SLionel Sambuc 		return OK;
2564*433d6423SLionel Sambuc 
2565*433d6423SLionel Sambuc 	/* The device is now fully closed. That also means that the threads for
2566*433d6423SLionel Sambuc 	 * this device are not needed anymore, so we reduce the count to one.
2567*433d6423SLionel Sambuc 	 */
2568*433d6423SLionel Sambuc 	blockdriver_mt_set_workers(ps->device, 1);
2569*433d6423SLionel Sambuc 
2570*433d6423SLionel Sambuc 	if (ps->state == STATE_GOOD_DEV && !(ps->flags & FLAG_BARRIER)) {
2571*433d6423SLionel Sambuc 		dprintf(V_INFO, ("%s: flushing write cache\n",
2572*433d6423SLionel Sambuc 			ahci_portname(ps)));
2573*433d6423SLionel Sambuc 
2574*433d6423SLionel Sambuc 		(void) gen_flush_wcache(ps);
2575*433d6423SLionel Sambuc 	}
2576*433d6423SLionel Sambuc 
2577*433d6423SLionel Sambuc 	/* If the entire driver has been told to terminate, check whether all
2578*433d6423SLionel Sambuc 	 * devices are now closed. If so, tell libblockdriver to quit after
2579*433d6423SLionel Sambuc 	 * replying to the close request.
2580*433d6423SLionel Sambuc 	 */
2581*433d6423SLionel Sambuc 	if (ahci_exiting) {
2582*433d6423SLionel Sambuc 		for (port = 0; port < hba_state.nr_ports; port++)
2583*433d6423SLionel Sambuc 			if (port_state[port].open_count > 0)
2584*433d6423SLionel Sambuc 				break;
2585*433d6423SLionel Sambuc 
2586*433d6423SLionel Sambuc 		if (port == hba_state.nr_ports) {
2587*433d6423SLionel Sambuc 			ahci_stop();
2588*433d6423SLionel Sambuc 
2589*433d6423SLionel Sambuc 			blockdriver_mt_terminate();
2590*433d6423SLionel Sambuc 		}
2591*433d6423SLionel Sambuc 	}
2592*433d6423SLionel Sambuc 
2593*433d6423SLionel Sambuc 	return OK;
2594*433d6423SLionel Sambuc }
2595*433d6423SLionel Sambuc 
2596*433d6423SLionel Sambuc /*===========================================================================*
2597*433d6423SLionel Sambuc  *				ahci_transfer				     *
2598*433d6423SLionel Sambuc  *===========================================================================*/
2599*433d6423SLionel Sambuc static ssize_t ahci_transfer(devminor_t minor, int do_write, u64_t position,
2600*433d6423SLionel Sambuc 	endpoint_t endpt, iovec_t *iovec, unsigned int count, int flags)
2601*433d6423SLionel Sambuc {
2602*433d6423SLionel Sambuc 	/* Perform data transfer on the selected device.
2603*433d6423SLionel Sambuc 	 */
2604*433d6423SLionel Sambuc 	struct port_state *ps;
2605*433d6423SLionel Sambuc 	struct device *dv;
2606*433d6423SLionel Sambuc 	u64_t pos, eof;
2607*433d6423SLionel Sambuc 
2608*433d6423SLionel Sambuc 	ps = ahci_get_port(minor);
2609*433d6423SLionel Sambuc 	dv = ahci_part(minor);
2610*433d6423SLionel Sambuc 
2611*433d6423SLionel Sambuc 	if (ps->state != STATE_GOOD_DEV || (ps->flags & FLAG_BARRIER))
2612*433d6423SLionel Sambuc 		return EIO;
2613*433d6423SLionel Sambuc 
2614*433d6423SLionel Sambuc 	if (count > NR_IOREQS)
2615*433d6423SLionel Sambuc 		return EINVAL;
2616*433d6423SLionel Sambuc 
2617*433d6423SLionel Sambuc 	/* Check for basic end-of-partition condition: if the start position of
2618*433d6423SLionel Sambuc 	 * the request is outside the partition, return success immediately.
2619*433d6423SLionel Sambuc 	 * The size of the request is obtained, and possibly reduced, later.
2620*433d6423SLionel Sambuc 	 */
2621*433d6423SLionel Sambuc 	if (position >= dv->dv_size)
2622*433d6423SLionel Sambuc 		return OK;
2623*433d6423SLionel Sambuc 
2624*433d6423SLionel Sambuc 	pos = dv->dv_base + position;
2625*433d6423SLionel Sambuc 	eof = dv->dv_base + dv->dv_size;
2626*433d6423SLionel Sambuc 
2627*433d6423SLionel Sambuc 	return port_transfer(ps, pos, eof, endpt, (iovec_s_t *) iovec, count,
2628*433d6423SLionel Sambuc 		do_write, flags);
2629*433d6423SLionel Sambuc }
2630*433d6423SLionel Sambuc 
2631*433d6423SLionel Sambuc /*===========================================================================*
2632*433d6423SLionel Sambuc  *				ahci_ioctl				     *
2633*433d6423SLionel Sambuc  *===========================================================================*/
2634*433d6423SLionel Sambuc static int ahci_ioctl(devminor_t minor, unsigned long request,
2635*433d6423SLionel Sambuc 	endpoint_t endpt, cp_grant_id_t grant, endpoint_t UNUSED(user_endpt))
2636*433d6423SLionel Sambuc {
2637*433d6423SLionel Sambuc 	/* Process I/O control requests.
2638*433d6423SLionel Sambuc 	 */
2639*433d6423SLionel Sambuc 	struct port_state *ps;
2640*433d6423SLionel Sambuc 	int r, val;
2641*433d6423SLionel Sambuc 
2642*433d6423SLionel Sambuc 	ps = ahci_get_port(minor);
2643*433d6423SLionel Sambuc 
2644*433d6423SLionel Sambuc 	switch (request) {
2645*433d6423SLionel Sambuc 	case DIOCEJECT:
2646*433d6423SLionel Sambuc 		if (ps->state != STATE_GOOD_DEV || (ps->flags & FLAG_BARRIER))
2647*433d6423SLionel Sambuc 			return EIO;
2648*433d6423SLionel Sambuc 
2649*433d6423SLionel Sambuc 		if (!(ps->flags & FLAG_ATAPI))
2650*433d6423SLionel Sambuc 			return EINVAL;
2651*433d6423SLionel Sambuc 
2652*433d6423SLionel Sambuc 		return atapi_load_eject(ps, 0, FALSE /*load*/);
2653*433d6423SLionel Sambuc 
2654*433d6423SLionel Sambuc 	case DIOCOPENCT:
2655*433d6423SLionel Sambuc 		return sys_safecopyto(endpt, grant, 0,
2656*433d6423SLionel Sambuc 			(vir_bytes) &ps->open_count, sizeof(ps->open_count));
2657*433d6423SLionel Sambuc 
2658*433d6423SLionel Sambuc 	case DIOCFLUSH:
2659*433d6423SLionel Sambuc 		if (ps->state != STATE_GOOD_DEV || (ps->flags & FLAG_BARRIER))
2660*433d6423SLionel Sambuc 			return EIO;
2661*433d6423SLionel Sambuc 
2662*433d6423SLionel Sambuc 		return gen_flush_wcache(ps);
2663*433d6423SLionel Sambuc 
2664*433d6423SLionel Sambuc 	case DIOCSETWC:
2665*433d6423SLionel Sambuc 		if (ps->state != STATE_GOOD_DEV || (ps->flags & FLAG_BARRIER))
2666*433d6423SLionel Sambuc 			return EIO;
2667*433d6423SLionel Sambuc 
2668*433d6423SLionel Sambuc 		if ((r = sys_safecopyfrom(endpt, grant, 0, (vir_bytes) &val,
2669*433d6423SLionel Sambuc 			sizeof(val))) != OK)
2670*433d6423SLionel Sambuc 			return r;
2671*433d6423SLionel Sambuc 
2672*433d6423SLionel Sambuc 		return gen_set_wcache(ps, val);
2673*433d6423SLionel Sambuc 
2674*433d6423SLionel Sambuc 	case DIOCGETWC:
2675*433d6423SLionel Sambuc 		if (ps->state != STATE_GOOD_DEV || (ps->flags & FLAG_BARRIER))
2676*433d6423SLionel Sambuc 			return EIO;
2677*433d6423SLionel Sambuc 
2678*433d6423SLionel Sambuc 		if ((r = gen_get_wcache(ps, &val)) != OK)
2679*433d6423SLionel Sambuc 			return r;
2680*433d6423SLionel Sambuc 
2681*433d6423SLionel Sambuc 		return sys_safecopyto(endpt, grant, 0, (vir_bytes) &val,
2682*433d6423SLionel Sambuc 			sizeof(val));
2683*433d6423SLionel Sambuc 	}
2684*433d6423SLionel Sambuc 
2685*433d6423SLionel Sambuc 	return ENOTTY;
2686*433d6423SLionel Sambuc }
2687*433d6423SLionel Sambuc 
2688*433d6423SLionel Sambuc /*===========================================================================*
2689*433d6423SLionel Sambuc  *				ahci_device				     *
2690*433d6423SLionel Sambuc  *===========================================================================*/
2691*433d6423SLionel Sambuc static int ahci_device(devminor_t minor, device_id_t *id)
2692*433d6423SLionel Sambuc {
2693*433d6423SLionel Sambuc 	/* Map a minor device number to a device ID.
2694*433d6423SLionel Sambuc 	 */
2695*433d6423SLionel Sambuc 	struct port_state *ps;
2696*433d6423SLionel Sambuc 	struct device *dv;
2697*433d6423SLionel Sambuc 
2698*433d6423SLionel Sambuc 	if ((ps = ahci_map_minor(minor, &dv)) == NULL)
2699*433d6423SLionel Sambuc 		return ENXIO;
2700*433d6423SLionel Sambuc 
2701*433d6423SLionel Sambuc 	*id = ps->device;
2702*433d6423SLionel Sambuc 
2703*433d6423SLionel Sambuc 	return OK;
2704*433d6423SLionel Sambuc }
2705*433d6423SLionel Sambuc 
2706*433d6423SLionel Sambuc /*===========================================================================*
2707*433d6423SLionel Sambuc  *				ahci_get_port				     *
2708*433d6423SLionel Sambuc  *===========================================================================*/
2709*433d6423SLionel Sambuc static struct port_state *ahci_get_port(devminor_t minor)
2710*433d6423SLionel Sambuc {
2711*433d6423SLionel Sambuc 	/* Get the port structure associated with the given minor device.
2712*433d6423SLionel Sambuc 	 * Called only from worker threads, so the minor device is already
2713*433d6423SLionel Sambuc 	 * guaranteed to map to a port.
2714*433d6423SLionel Sambuc 	 */
2715*433d6423SLionel Sambuc 	struct port_state *ps;
2716*433d6423SLionel Sambuc 	struct device *dv;
2717*433d6423SLionel Sambuc 
2718*433d6423SLionel Sambuc 	if ((ps = ahci_map_minor(minor, &dv)) == NULL)
2719*433d6423SLionel Sambuc 		panic("device mapping for minor %d disappeared", minor);
2720*433d6423SLionel Sambuc 
2721*433d6423SLionel Sambuc 	return ps;
2722*433d6423SLionel Sambuc }
2723*433d6423SLionel Sambuc 
2724*433d6423SLionel Sambuc /*===========================================================================*
2725*433d6423SLionel Sambuc  *				main					     *
2726*433d6423SLionel Sambuc  *===========================================================================*/
2727*433d6423SLionel Sambuc int main(int argc, char **argv)
2728*433d6423SLionel Sambuc {
2729*433d6423SLionel Sambuc 	/* Driver task.
2730*433d6423SLionel Sambuc 	 */
2731*433d6423SLionel Sambuc 
2732*433d6423SLionel Sambuc 	env_setargs(argc, argv);
2733*433d6423SLionel Sambuc 	sef_local_startup();
2734*433d6423SLionel Sambuc 
2735*433d6423SLionel Sambuc 	blockdriver_mt_task(&ahci_dtab);
2736*433d6423SLionel Sambuc 
2737*433d6423SLionel Sambuc 	return 0;
2738*433d6423SLionel Sambuc }
2739