1433d6423SLionel Sambuc /* Advanced Host Controller Interface (AHCI) driver, by D.C. van Moolenbroek
2433d6423SLionel Sambuc * - Multithreading support by Arne Welzel
3433d6423SLionel Sambuc * - Native Command Queuing support by Raja Appuswamy
4433d6423SLionel Sambuc */
5433d6423SLionel Sambuc /*
6433d6423SLionel Sambuc * This driver is based on the following specifications:
7433d6423SLionel Sambuc * - Serial ATA Advanced Host Controller Interface (AHCI) 1.3
8433d6423SLionel Sambuc * - Serial ATA Revision 2.6
9433d6423SLionel Sambuc * - AT Attachment with Packet Interface 7 (ATA/ATAPI-7)
10433d6423SLionel Sambuc * - ATAPI Removable Rewritable Media Devices 1.3 (SFF-8070)
11433d6423SLionel Sambuc *
12433d6423SLionel Sambuc * The driver supports device hot-plug, active device status tracking,
13433d6423SLionel Sambuc * nonremovable ATA and removable ATAPI devices, custom logical sector sizes,
14433d6423SLionel Sambuc * sector-unaligned reads, native command queuing and parallel requests to
15433d6423SLionel Sambuc * different devices.
16433d6423SLionel Sambuc *
17433d6423SLionel Sambuc * It does not implement transparent failure recovery, power management, or
18433d6423SLionel Sambuc * port multiplier support.
19433d6423SLionel Sambuc */
20433d6423SLionel Sambuc /*
21433d6423SLionel Sambuc * An AHCI controller exposes a number of ports (up to 32), each of which may
22433d6423SLionel Sambuc * or may not have one device attached (port multipliers are not supported).
23433d6423SLionel Sambuc * Each port is maintained independently.
24433d6423SLionel Sambuc *
25433d6423SLionel Sambuc * The following figure depicts the possible transitions between port states.
26433d6423SLionel Sambuc * The NO_PORT state is not included; no transitions can be made from or to it.
27433d6423SLionel Sambuc *
28433d6423SLionel Sambuc * +----------+ +----------+
29433d6423SLionel Sambuc * | SPIN_UP | ------+ +-----> | BAD_DEV | ------------------+
30433d6423SLionel Sambuc * +----------+ | | +----------+ |
31433d6423SLionel Sambuc * | | | ^ |
32433d6423SLionel Sambuc * v v | | |
33433d6423SLionel Sambuc * +----------+ +----------+ +----------+ +----------+ |
34433d6423SLionel Sambuc * | NO_DEV | --> | WAIT_DEV | --> | WAIT_ID | --> | GOOD_DEV | |
35433d6423SLionel Sambuc * +----------+ +----------+ +----------+ +----------+ |
36433d6423SLionel Sambuc * ^ | | | |
37433d6423SLionel Sambuc * +----------------+----------------+----------------+--------+
38433d6423SLionel Sambuc *
39433d6423SLionel Sambuc * At driver startup, all physically present ports are put in SPIN_UP state.
40433d6423SLionel Sambuc * This state differs from NO_DEV in that BDEV_OPEN calls will be deferred
41433d6423SLionel Sambuc * until either the spin-up timer expires, or a device has been identified on
42433d6423SLionel Sambuc * that port. This prevents early BDEV_OPEN calls from failing erroneously at
43433d6423SLionel Sambuc * startup time if the device has not yet been able to announce its presence.
44433d6423SLionel Sambuc *
45433d6423SLionel Sambuc * If a device is detected, either at startup time or after hot-plug, its
46433d6423SLionel Sambuc * signature is checked and it is identified, after which it may be determined
47433d6423SLionel Sambuc * to be a usable ("good") device, which means that the device is considered to
48433d6423SLionel Sambuc * be in a working state. If these steps fail, the device is marked as unusable
49433d6423SLionel Sambuc * ("bad"). At any point in time, the device may be disconnected; the port is
50433d6423SLionel Sambuc * then put back into NO_DEV state.
51433d6423SLionel Sambuc *
52433d6423SLionel Sambuc * A device in working state (GOOD_DEV) may or may not have a medium. All ATA
53433d6423SLionel Sambuc * devices are assumed to be fixed; all ATAPI devices are assumed to have
54433d6423SLionel Sambuc * removable media. To prevent erroneous access to switched devices and media,
55433d6423SLionel Sambuc * the driver makes devices inaccessible until they are fully closed (the open
56433d6423SLionel Sambuc * count is zero) when a device (hot-plug) or medium change is detected.
57433d6423SLionel Sambuc * For hot-plug changes, access is prevented by setting the BARRIER flag until
58433d6423SLionel Sambuc * the device is fully closed and then reopened. For medium changes, access is
59433d6423SLionel Sambuc * prevented by not acknowledging the medium change until the device is fully
60433d6423SLionel Sambuc * closed and reopened. Removable media are not locked in the drive while
61433d6423SLionel Sambuc * opened, because the driver author is uncomfortable with that concept.
62433d6423SLionel Sambuc *
63433d6423SLionel Sambuc * Ports may leave the group of states where a device is connected (that is,
64433d6423SLionel Sambuc * WAIT_ID, GOOD_DEV, and BAD_DEV) in two ways: either due to a hot-unplug
65433d6423SLionel Sambuc * event, or due to a hard reset after a serious failure. For simplicity, we
66433d6423SLionel Sambuc * we perform a hard reset after a hot-unplug event as well, so that the link
67433d6423SLionel Sambuc * to the device is broken. Thus, in both cases, a transition to NO_DEV is
68433d6423SLionel Sambuc * made, after which the link to the device may or may not be reestablished.
69433d6423SLionel Sambuc * In both cases, ongoing requests are cancelled and the BARRIER flag is set.
70433d6423SLionel Sambuc *
71433d6423SLionel Sambuc * The following table lists for each state, whether the port is started
72433d6423SLionel Sambuc * (PxCMD.ST is set), whether a timer is running, what the PxIE mask is to be
73433d6423SLionel Sambuc * set to, and what BDEV_OPEN calls on this port should return.
74433d6423SLionel Sambuc *
75433d6423SLionel Sambuc * State Started Timer PxIE BDEV_OPEN
76433d6423SLionel Sambuc * --------- --------- --------- --------- ---------
77433d6423SLionel Sambuc * NO_PORT no no (none) ENXIO
78433d6423SLionel Sambuc * SPIN_UP no yes PCE (wait)
79433d6423SLionel Sambuc * NO_DEV no no PCE ENXIO
80433d6423SLionel Sambuc * WAIT_DEV no yes PCE (wait)
81433d6423SLionel Sambuc * BAD_DEV no no PRCE ENXIO
82433d6423SLionel Sambuc * WAIT_ID yes yes PRCE+ (wait)
83433d6423SLionel Sambuc * GOOD_DEV yes per-command PRCE+ OK
84433d6423SLionel Sambuc *
85433d6423SLionel Sambuc * In order to continue deferred BDEV_OPEN calls, the BUSY flag must be unset
86433d6423SLionel Sambuc * when changing from SPIN_UP to any state but WAIT_DEV, and when changing from
87433d6423SLionel Sambuc * WAIT_DEV to any state but WAIT_ID, and when changing from WAIT_ID to any
88433d6423SLionel Sambuc * other state.
89433d6423SLionel Sambuc */
90433d6423SLionel Sambuc /*
91433d6423SLionel Sambuc * The maximum byte size of a single transfer (MAX_TRANSFER) is currently set
92433d6423SLionel Sambuc * to 4MB. This limit has been chosen for a number of reasons:
93433d6423SLionel Sambuc * - The size that can be specified in a Physical Region Descriptor (PRD) is
94433d6423SLionel Sambuc * limited to 4MB for AHCI. Limiting the total transfer size to at most this
95433d6423SLionel Sambuc * size implies that no I/O vector element needs to be split up across PRDs.
96433d6423SLionel Sambuc * This means that the maximum number of needed PRDs can be predetermined.
97433d6423SLionel Sambuc * - The limit is below what can be transferred in a single ATA request, namely
98433d6423SLionel Sambuc * 64k sectors (i.e., at least 32MB). This means that transfer requests need
99433d6423SLionel Sambuc * never be split up into smaller chunks, reducing implementation complexity.
100433d6423SLionel Sambuc * - A single, static timeout can be used for transfers. Very large transfers
101433d6423SLionel Sambuc * can legitimately take up to several minutes -- well beyond the appropriate
102433d6423SLionel Sambuc * timeout range for small transfers. The limit obviates the need for a
103433d6423SLionel Sambuc * timeout scheme that takes into account the transfer size.
104433d6423SLionel Sambuc * - Similarly, the transfer limit reduces the opportunity for buggy/malicious
105433d6423SLionel Sambuc * clients to keep the driver busy for a long time with a single request.
106433d6423SLionel Sambuc * - The limit is high enough for all practical purposes. The transfer setup
107433d6423SLionel Sambuc * overhead is already relatively negligible at this size, and even larger
108433d6423SLionel Sambuc * requests will not help maximize throughput. As NR_IOREQS is currently set
109433d6423SLionel Sambuc * to 64, the limit still allows file systems to perform I/O requests with
110433d6423SLionel Sambuc * vectors completely filled with 64KB-blocks.
111433d6423SLionel Sambuc */
112433d6423SLionel Sambuc #include <minix/drivers.h>
113433d6423SLionel Sambuc #include <minix/blockdriver_mt.h>
114433d6423SLionel Sambuc #include <minix/drvlib.h>
115433d6423SLionel Sambuc #include <machine/pci.h>
116433d6423SLionel Sambuc #include <sys/ioc_disk.h>
117433d6423SLionel Sambuc #include <sys/mman.h>
118433d6423SLionel Sambuc #include <assert.h>
119433d6423SLionel Sambuc
120433d6423SLionel Sambuc #include "ahci.h"
121433d6423SLionel Sambuc
122433d6423SLionel Sambuc /* Host Bus Adapter (HBA) state. */
123433d6423SLionel Sambuc static struct {
124433d6423SLionel Sambuc volatile u32_t *base; /* base address of memory-mapped registers */
125433d6423SLionel Sambuc size_t size; /* size of memory-mapped register area */
126433d6423SLionel Sambuc
127433d6423SLionel Sambuc int nr_ports; /* addressable number of ports (1..NR_PORTS) */
128433d6423SLionel Sambuc int nr_cmds; /* maximum number of commands per port */
129433d6423SLionel Sambuc int has_ncq; /* NCQ support flag */
130433d6423SLionel Sambuc int has_clo; /* CLO support flag */
131433d6423SLionel Sambuc
132433d6423SLionel Sambuc int irq; /* IRQ number */
133433d6423SLionel Sambuc int hook_id; /* IRQ hook ID */
134433d6423SLionel Sambuc } hba_state;
135433d6423SLionel Sambuc
136433d6423SLionel Sambuc #define hba_read(r) (hba_state.base[r])
137433d6423SLionel Sambuc #define hba_write(r, v) (hba_state.base[r] = (v))
138433d6423SLionel Sambuc
139433d6423SLionel Sambuc /* Port state. */
140433d6423SLionel Sambuc static struct port_state {
141433d6423SLionel Sambuc int state; /* port state */
142433d6423SLionel Sambuc unsigned int flags; /* port flags */
143433d6423SLionel Sambuc
144433d6423SLionel Sambuc volatile u32_t *reg; /* memory-mapped port registers */
145433d6423SLionel Sambuc
146433d6423SLionel Sambuc u8_t *mem_base; /* primary memory buffer virtual address */
147433d6423SLionel Sambuc phys_bytes mem_phys; /* primary memory buffer physical address */
148433d6423SLionel Sambuc vir_bytes mem_size; /* primary memory buffer size */
149433d6423SLionel Sambuc
150433d6423SLionel Sambuc /* the FIS, CL, CT[0] and TMP buffers are all in the primary buffer */
151433d6423SLionel Sambuc u32_t *fis_base; /* FIS receive buffer virtual address */
152433d6423SLionel Sambuc phys_bytes fis_phys; /* FIS receive buffer physical address */
153433d6423SLionel Sambuc u32_t *cl_base; /* command list buffer virtual address */
154433d6423SLionel Sambuc phys_bytes cl_phys; /* command list buffer physical address */
155433d6423SLionel Sambuc u8_t *ct_base[NR_CMDS]; /* command table virtual address */
156433d6423SLionel Sambuc phys_bytes ct_phys[NR_CMDS]; /* command table physical address */
157433d6423SLionel Sambuc u8_t *tmp_base; /* temporary storage buffer virtual address */
158433d6423SLionel Sambuc phys_bytes tmp_phys; /* temporary storage buffer physical address */
159433d6423SLionel Sambuc
160433d6423SLionel Sambuc u8_t *pad_base; /* sector padding buffer virtual address */
161433d6423SLionel Sambuc phys_bytes pad_phys; /* sector padding buffer physical address */
162433d6423SLionel Sambuc vir_bytes pad_size; /* sector padding buffer size */
163433d6423SLionel Sambuc
164433d6423SLionel Sambuc u64_t lba_count; /* number of valid Logical Block Addresses */
165433d6423SLionel Sambuc u32_t sector_size; /* medium sector size in bytes */
166433d6423SLionel Sambuc
167433d6423SLionel Sambuc int open_count; /* number of times this port is opened */
168433d6423SLionel Sambuc
169433d6423SLionel Sambuc int device; /* associated device number, or NO_DEVICE */
170433d6423SLionel Sambuc struct device part[DEV_PER_DRIVE]; /* partition bases and sizes */
171433d6423SLionel Sambuc struct device subpart[SUB_PER_DRIVE]; /* same for subpartitions */
172433d6423SLionel Sambuc
173433d6423SLionel Sambuc minix_timer_t timer; /* port-specific timeout timer */
174433d6423SLionel Sambuc int left; /* number of tries left before giving up */
175433d6423SLionel Sambuc /* (only used for signature probing) */
176433d6423SLionel Sambuc
177433d6423SLionel Sambuc int queue_depth; /* NCQ queue depth */
178433d6423SLionel Sambuc u32_t pend_mask; /* commands not yet complete */
179433d6423SLionel Sambuc struct {
180433d6423SLionel Sambuc thread_id_t tid;/* ID of the worker thread */
181433d6423SLionel Sambuc minix_timer_t timer; /* timer associated with each request */
182433d6423SLionel Sambuc int result; /* success/failure result of the commands */
183433d6423SLionel Sambuc } cmd_info[NR_CMDS];
184433d6423SLionel Sambuc } port_state[NR_PORTS];
185433d6423SLionel Sambuc
186433d6423SLionel Sambuc #define port_read(ps, r) ((ps)->reg[r])
187433d6423SLionel Sambuc #define port_write(ps, r, v) ((ps)->reg[r] = (v))
188433d6423SLionel Sambuc
189433d6423SLionel Sambuc static int ahci_instance; /* driver instance number */
190433d6423SLionel Sambuc
191433d6423SLionel Sambuc static int ahci_verbose; /* verbosity level (0..4) */
192433d6423SLionel Sambuc
193433d6423SLionel Sambuc /* Timeout-related values. */
194433d6423SLionel Sambuc static clock_t ahci_spinup_timeout;
195433d6423SLionel Sambuc static clock_t ahci_device_timeout;
196433d6423SLionel Sambuc static clock_t ahci_device_delay;
197433d6423SLionel Sambuc static unsigned int ahci_device_checks;
198433d6423SLionel Sambuc static clock_t ahci_command_timeout;
199433d6423SLionel Sambuc static clock_t ahci_transfer_timeout;
200433d6423SLionel Sambuc static clock_t ahci_flush_timeout;
201433d6423SLionel Sambuc
202433d6423SLionel Sambuc /* Timeout environment variable names and default values. */
203433d6423SLionel Sambuc static struct {
204433d6423SLionel Sambuc char *name; /* environment variable name */
205433d6423SLionel Sambuc u32_t default_ms; /* default in milliseconds */
206433d6423SLionel Sambuc clock_t *ptr; /* clock ticks value pointer */
207433d6423SLionel Sambuc } ahci_timevar[] = {
208433d6423SLionel Sambuc { "ahci_init_timeout", SPINUP_TIMEOUT, &ahci_spinup_timeout },
209433d6423SLionel Sambuc { "ahci_device_timeout", DEVICE_TIMEOUT, &ahci_device_timeout },
210433d6423SLionel Sambuc { "ahci_cmd_timeout", COMMAND_TIMEOUT, &ahci_command_timeout },
211433d6423SLionel Sambuc { "ahci_io_timeout", TRANSFER_TIMEOUT, &ahci_transfer_timeout },
212433d6423SLionel Sambuc { "ahci_flush_timeout", FLUSH_TIMEOUT, &ahci_flush_timeout }
213433d6423SLionel Sambuc };
214433d6423SLionel Sambuc
215433d6423SLionel Sambuc static int ahci_map[MAX_DRIVES]; /* device-to-port mapping */
216433d6423SLionel Sambuc
217433d6423SLionel Sambuc static int ahci_exiting = FALSE; /* exit after last close? */
218433d6423SLionel Sambuc
219433d6423SLionel Sambuc #define BUILD_ARG(port, tag) (((port) << 8) | (tag))
220433d6423SLionel Sambuc #define GET_PORT(arg) ((arg) >> 8)
221433d6423SLionel Sambuc #define GET_TAG(arg) ((arg) & 0xFF)
222433d6423SLionel Sambuc
223433d6423SLionel Sambuc #define dprintf(v,s) do { \
224433d6423SLionel Sambuc if (ahci_verbose >= (v)) \
225433d6423SLionel Sambuc printf s; \
226433d6423SLionel Sambuc } while (0)
227433d6423SLionel Sambuc
228433d6423SLionel Sambuc /* Convert milliseconds to clock ticks. Round up. */
229433d6423SLionel Sambuc #define millis_to_hz(ms) (((ms) * sys_hz() + 999) / 1000)
230433d6423SLionel Sambuc
231433d6423SLionel Sambuc static void port_set_cmd(struct port_state *ps, int cmd, cmd_fis_t *fis,
232433d6423SLionel Sambuc u8_t packet[ATAPI_PACKET_SIZE], prd_t *prdt, int nr_prds, int write);
233433d6423SLionel Sambuc static void port_issue(struct port_state *ps, int cmd, clock_t timeout);
234433d6423SLionel Sambuc static int port_exec(struct port_state *ps, int cmd, clock_t timeout);
235*cfd712b4SDavid van Moolenbroek static void port_timeout(int arg);
236433d6423SLionel Sambuc static void port_disconnect(struct port_state *ps);
237433d6423SLionel Sambuc
238433d6423SLionel Sambuc static char *ahci_portname(struct port_state *ps);
239433d6423SLionel Sambuc static int ahci_open(devminor_t minor, int access);
240433d6423SLionel Sambuc static int ahci_close(devminor_t minor);
241433d6423SLionel Sambuc static ssize_t ahci_transfer(devminor_t minor, int do_write, u64_t position,
242433d6423SLionel Sambuc endpoint_t endpt, iovec_t *iovec, unsigned int count, int flags);
243433d6423SLionel Sambuc static struct device *ahci_part(devminor_t minor);
244433d6423SLionel Sambuc static void ahci_alarm(clock_t stamp);
245433d6423SLionel Sambuc static int ahci_ioctl(devminor_t minor, unsigned long request,
246433d6423SLionel Sambuc endpoint_t endpt, cp_grant_id_t grant, endpoint_t user_endpt);
247433d6423SLionel Sambuc static void ahci_intr(unsigned int mask);
248433d6423SLionel Sambuc static int ahci_device(devminor_t minor, device_id_t *id);
249433d6423SLionel Sambuc static struct port_state *ahci_get_port(devminor_t minor);
250433d6423SLionel Sambuc
251433d6423SLionel Sambuc /* AHCI driver table. */
252433d6423SLionel Sambuc static struct blockdriver ahci_dtab = {
253433d6423SLionel Sambuc .bdr_type = BLOCKDRIVER_TYPE_DISK,
254433d6423SLionel Sambuc .bdr_open = ahci_open,
255433d6423SLionel Sambuc .bdr_close = ahci_close,
256433d6423SLionel Sambuc .bdr_transfer = ahci_transfer,
257433d6423SLionel Sambuc .bdr_ioctl = ahci_ioctl,
258433d6423SLionel Sambuc .bdr_part = ahci_part,
259433d6423SLionel Sambuc .bdr_intr = ahci_intr,
260433d6423SLionel Sambuc .bdr_alarm = ahci_alarm,
261433d6423SLionel Sambuc .bdr_device = ahci_device
262433d6423SLionel Sambuc };
263433d6423SLionel Sambuc
264433d6423SLionel Sambuc /*===========================================================================*
265433d6423SLionel Sambuc * atapi_exec *
266433d6423SLionel Sambuc *===========================================================================*/
atapi_exec(struct port_state * ps,int cmd,u8_t packet[ATAPI_PACKET_SIZE],size_t size,int write)267433d6423SLionel Sambuc static int atapi_exec(struct port_state *ps, int cmd,
268433d6423SLionel Sambuc u8_t packet[ATAPI_PACKET_SIZE], size_t size, int write)
269433d6423SLionel Sambuc {
270433d6423SLionel Sambuc /* Execute an ATAPI command. Return OK or error.
271433d6423SLionel Sambuc */
272433d6423SLionel Sambuc cmd_fis_t fis;
273433d6423SLionel Sambuc prd_t prd[1];
274433d6423SLionel Sambuc int nr_prds = 0;
275433d6423SLionel Sambuc
276433d6423SLionel Sambuc assert(size <= AHCI_TMP_SIZE);
277433d6423SLionel Sambuc
278433d6423SLionel Sambuc /* Fill in the command table with a FIS, a packet, and if a data
279433d6423SLionel Sambuc * transfer is requested, also a PRD.
280433d6423SLionel Sambuc */
281433d6423SLionel Sambuc memset(&fis, 0, sizeof(fis));
282433d6423SLionel Sambuc fis.cf_cmd = ATA_CMD_PACKET;
283433d6423SLionel Sambuc
284433d6423SLionel Sambuc if (size > 0) {
285433d6423SLionel Sambuc fis.cf_feat = ATA_FEAT_PACKET_DMA;
286433d6423SLionel Sambuc if (!write && (ps->flags & FLAG_USE_DMADIR))
287433d6423SLionel Sambuc fis.cf_feat |= ATA_FEAT_PACKET_DMADIR;
288433d6423SLionel Sambuc
289433d6423SLionel Sambuc prd[0].vp_addr = ps->tmp_phys;
290433d6423SLionel Sambuc prd[0].vp_size = size;
291433d6423SLionel Sambuc nr_prds++;
292433d6423SLionel Sambuc }
293433d6423SLionel Sambuc
294433d6423SLionel Sambuc /* Start the command, and wait for it to complete or fail. */
295433d6423SLionel Sambuc port_set_cmd(ps, cmd, &fis, packet, prd, nr_prds, write);
296433d6423SLionel Sambuc
297433d6423SLionel Sambuc return port_exec(ps, cmd, ahci_command_timeout);
298433d6423SLionel Sambuc }
299433d6423SLionel Sambuc
300433d6423SLionel Sambuc /*===========================================================================*
301433d6423SLionel Sambuc * atapi_test_unit *
302433d6423SLionel Sambuc *===========================================================================*/
atapi_test_unit(struct port_state * ps,int cmd)303433d6423SLionel Sambuc static int atapi_test_unit(struct port_state *ps, int cmd)
304433d6423SLionel Sambuc {
305433d6423SLionel Sambuc /* Test whether the ATAPI device and medium are ready.
306433d6423SLionel Sambuc */
307433d6423SLionel Sambuc u8_t packet[ATAPI_PACKET_SIZE];
308433d6423SLionel Sambuc
309433d6423SLionel Sambuc memset(packet, 0, sizeof(packet));
310433d6423SLionel Sambuc packet[0] = ATAPI_CMD_TEST_UNIT;
311433d6423SLionel Sambuc
312433d6423SLionel Sambuc return atapi_exec(ps, cmd, packet, 0, FALSE);
313433d6423SLionel Sambuc }
314433d6423SLionel Sambuc
315433d6423SLionel Sambuc /*===========================================================================*
316433d6423SLionel Sambuc * atapi_request_sense *
317433d6423SLionel Sambuc *===========================================================================*/
atapi_request_sense(struct port_state * ps,int cmd,int * sense)318433d6423SLionel Sambuc static int atapi_request_sense(struct port_state *ps, int cmd, int *sense)
319433d6423SLionel Sambuc {
320433d6423SLionel Sambuc /* Request error (sense) information from an ATAPI device, and return
321433d6423SLionel Sambuc * the sense key. The additional sense codes are not used at this time.
322433d6423SLionel Sambuc */
323433d6423SLionel Sambuc u8_t packet[ATAPI_PACKET_SIZE];
324433d6423SLionel Sambuc int r;
325433d6423SLionel Sambuc
326433d6423SLionel Sambuc memset(packet, 0, sizeof(packet));
327433d6423SLionel Sambuc packet[0] = ATAPI_CMD_REQUEST_SENSE;
328433d6423SLionel Sambuc packet[4] = ATAPI_REQUEST_SENSE_LEN;
329433d6423SLionel Sambuc
330433d6423SLionel Sambuc r = atapi_exec(ps, cmd, packet, ATAPI_REQUEST_SENSE_LEN, FALSE);
331433d6423SLionel Sambuc
332433d6423SLionel Sambuc if (r != OK)
333433d6423SLionel Sambuc return r;
334433d6423SLionel Sambuc
335433d6423SLionel Sambuc dprintf(V_REQ, ("%s: ATAPI SENSE: sense %x ASC %x ASCQ %x\n",
336433d6423SLionel Sambuc ahci_portname(ps), ps->tmp_base[2] & 0xF, ps->tmp_base[12],
337433d6423SLionel Sambuc ps->tmp_base[13]));
338433d6423SLionel Sambuc
339433d6423SLionel Sambuc *sense = ps->tmp_base[2] & 0xF;
340433d6423SLionel Sambuc
341433d6423SLionel Sambuc return OK;
342433d6423SLionel Sambuc }
343433d6423SLionel Sambuc
344433d6423SLionel Sambuc /*===========================================================================*
345433d6423SLionel Sambuc * atapi_load_eject *
346433d6423SLionel Sambuc *===========================================================================*/
atapi_load_eject(struct port_state * ps,int cmd,int load)347433d6423SLionel Sambuc static int atapi_load_eject(struct port_state *ps, int cmd, int load)
348433d6423SLionel Sambuc {
349433d6423SLionel Sambuc /* Load or eject a medium in an ATAPI device.
350433d6423SLionel Sambuc */
351433d6423SLionel Sambuc u8_t packet[ATAPI_PACKET_SIZE];
352433d6423SLionel Sambuc
353433d6423SLionel Sambuc memset(packet, 0, sizeof(packet));
354433d6423SLionel Sambuc packet[0] = ATAPI_CMD_START_STOP;
355433d6423SLionel Sambuc packet[4] = load ? ATAPI_START_STOP_LOAD : ATAPI_START_STOP_EJECT;
356433d6423SLionel Sambuc
357433d6423SLionel Sambuc return atapi_exec(ps, cmd, packet, 0, FALSE);
358433d6423SLionel Sambuc }
359433d6423SLionel Sambuc
360433d6423SLionel Sambuc /*===========================================================================*
361433d6423SLionel Sambuc * atapi_read_capacity *
362433d6423SLionel Sambuc *===========================================================================*/
atapi_read_capacity(struct port_state * ps,int cmd)363433d6423SLionel Sambuc static int atapi_read_capacity(struct port_state *ps, int cmd)
364433d6423SLionel Sambuc {
365433d6423SLionel Sambuc /* Retrieve the LBA count and sector size of an ATAPI medium.
366433d6423SLionel Sambuc */
367433d6423SLionel Sambuc u8_t packet[ATAPI_PACKET_SIZE], *buf;
368433d6423SLionel Sambuc int r;
369433d6423SLionel Sambuc
370433d6423SLionel Sambuc memset(packet, 0, sizeof(packet));
371433d6423SLionel Sambuc packet[0] = ATAPI_CMD_READ_CAPACITY;
372433d6423SLionel Sambuc
373433d6423SLionel Sambuc r = atapi_exec(ps, cmd, packet, ATAPI_READ_CAPACITY_LEN, FALSE);
374433d6423SLionel Sambuc if (r != OK)
375433d6423SLionel Sambuc return r;
376433d6423SLionel Sambuc
377433d6423SLionel Sambuc /* Store the number of LBA blocks and sector size. */
378433d6423SLionel Sambuc buf = ps->tmp_base;
379433d6423SLionel Sambuc ps->lba_count = (u64_t) ((buf[0] << 24) | (buf[1] << 16) |
380433d6423SLionel Sambuc (buf[2] << 8) | buf[3]) + 1;
381433d6423SLionel Sambuc ps->sector_size =
382433d6423SLionel Sambuc (buf[4] << 24) | (buf[5] << 16) | (buf[6] << 8) | buf[7];
383433d6423SLionel Sambuc
384433d6423SLionel Sambuc if (ps->sector_size == 0 || (ps->sector_size & 1)) {
385433d6423SLionel Sambuc dprintf(V_ERR, ("%s: invalid medium sector size %u\n",
386433d6423SLionel Sambuc ahci_portname(ps), ps->sector_size));
387433d6423SLionel Sambuc
388433d6423SLionel Sambuc return EINVAL;
389433d6423SLionel Sambuc }
390433d6423SLionel Sambuc
391433d6423SLionel Sambuc dprintf(V_INFO,
392433d6423SLionel Sambuc ("%s: medium detected (%u byte sectors, %llu MB size)\n",
393433d6423SLionel Sambuc ahci_portname(ps), ps->sector_size,
394433d6423SLionel Sambuc ps->lba_count * ps->sector_size / (1024*1024)));
395433d6423SLionel Sambuc
396433d6423SLionel Sambuc return OK;
397433d6423SLionel Sambuc }
398433d6423SLionel Sambuc
399433d6423SLionel Sambuc /*===========================================================================*
400433d6423SLionel Sambuc * atapi_check_medium *
401433d6423SLionel Sambuc *===========================================================================*/
atapi_check_medium(struct port_state * ps,int cmd)402433d6423SLionel Sambuc static int atapi_check_medium(struct port_state *ps, int cmd)
403433d6423SLionel Sambuc {
404433d6423SLionel Sambuc /* Check whether a medium is present in a removable-media ATAPI device.
405433d6423SLionel Sambuc * If a new medium is detected, get its total and sector size. Return
406433d6423SLionel Sambuc * OK only if a usable medium is present, and an error otherwise.
407433d6423SLionel Sambuc */
408433d6423SLionel Sambuc int sense;
409433d6423SLionel Sambuc
410433d6423SLionel Sambuc /* Perform a readiness check. */
411433d6423SLionel Sambuc if (atapi_test_unit(ps, cmd) != OK) {
412433d6423SLionel Sambuc ps->flags &= ~FLAG_HAS_MEDIUM;
413433d6423SLionel Sambuc
414433d6423SLionel Sambuc /* If the check failed due to a unit attention condition, retry
415433d6423SLionel Sambuc * reading the medium capacity. Otherwise, assume that there is
416433d6423SLionel Sambuc * no medium available.
417433d6423SLionel Sambuc */
418433d6423SLionel Sambuc if (atapi_request_sense(ps, cmd, &sense) != OK ||
419433d6423SLionel Sambuc sense != ATAPI_SENSE_UNIT_ATT)
420433d6423SLionel Sambuc return ENXIO;
421433d6423SLionel Sambuc }
422433d6423SLionel Sambuc
423433d6423SLionel Sambuc /* If a medium is newly detected, try reading its capacity now. */
424433d6423SLionel Sambuc if (!(ps->flags & FLAG_HAS_MEDIUM)) {
425433d6423SLionel Sambuc if (atapi_read_capacity(ps, cmd) != OK)
426433d6423SLionel Sambuc return EIO;
427433d6423SLionel Sambuc
428433d6423SLionel Sambuc ps->flags |= FLAG_HAS_MEDIUM;
429433d6423SLionel Sambuc }
430433d6423SLionel Sambuc
431433d6423SLionel Sambuc return OK;
432433d6423SLionel Sambuc }
433433d6423SLionel Sambuc
434433d6423SLionel Sambuc /*===========================================================================*
435433d6423SLionel Sambuc * atapi_id_check *
436433d6423SLionel Sambuc *===========================================================================*/
atapi_id_check(struct port_state * ps,u16_t * buf)437433d6423SLionel Sambuc static int atapi_id_check(struct port_state *ps, u16_t *buf)
438433d6423SLionel Sambuc {
439433d6423SLionel Sambuc /* Determine whether we support this ATAPI device based on the
440433d6423SLionel Sambuc * identification data it returned, and store some of its properties.
441433d6423SLionel Sambuc */
442433d6423SLionel Sambuc
443433d6423SLionel Sambuc /* The device must be an ATAPI device; it must have removable media;
444433d6423SLionel Sambuc * it must support DMA without DMADIR, or DMADIR for DMA.
445433d6423SLionel Sambuc */
446433d6423SLionel Sambuc if ((buf[ATA_ID_GCAP] & (ATA_ID_GCAP_ATAPI_MASK |
447433d6423SLionel Sambuc ATA_ID_GCAP_REMOVABLE | ATA_ID_GCAP_INCOMPLETE)) !=
448433d6423SLionel Sambuc (ATA_ID_GCAP_ATAPI | ATA_ID_GCAP_REMOVABLE) ||
449433d6423SLionel Sambuc ((buf[ATA_ID_CAP] & ATA_ID_CAP_DMA) != ATA_ID_CAP_DMA &&
450433d6423SLionel Sambuc (buf[ATA_ID_DMADIR] & (ATA_ID_DMADIR_DMADIR |
451433d6423SLionel Sambuc ATA_ID_DMADIR_DMA)) != (ATA_ID_DMADIR_DMADIR |
452433d6423SLionel Sambuc ATA_ID_DMADIR_DMA))) {
453433d6423SLionel Sambuc
454433d6423SLionel Sambuc dprintf(V_ERR, ("%s: unsupported ATAPI device\n",
455433d6423SLionel Sambuc ahci_portname(ps)));
456433d6423SLionel Sambuc
457433d6423SLionel Sambuc dprintf(V_DEV, ("%s: GCAP %04x CAP %04x DMADIR %04x\n",
458433d6423SLionel Sambuc ahci_portname(ps), buf[ATA_ID_GCAP], buf[ATA_ID_CAP],
459433d6423SLionel Sambuc buf[ATA_ID_DMADIR]));
460433d6423SLionel Sambuc
461433d6423SLionel Sambuc return FALSE;
462433d6423SLionel Sambuc }
463433d6423SLionel Sambuc
464433d6423SLionel Sambuc /* Remember whether to use the DMADIR flag when appropriate. */
465433d6423SLionel Sambuc if (buf[ATA_ID_DMADIR] & ATA_ID_DMADIR_DMADIR)
466433d6423SLionel Sambuc ps->flags |= FLAG_USE_DMADIR;
467433d6423SLionel Sambuc
468433d6423SLionel Sambuc /* ATAPI CD-ROM devices are considered read-only. */
469433d6423SLionel Sambuc if (((buf[ATA_ID_GCAP] & ATA_ID_GCAP_TYPE_MASK) >>
470433d6423SLionel Sambuc ATA_ID_GCAP_TYPE_SHIFT) == ATAPI_TYPE_CDROM)
471433d6423SLionel Sambuc ps->flags |= FLAG_READONLY;
472433d6423SLionel Sambuc
473433d6423SLionel Sambuc if ((buf[ATA_ID_SUP1] & ATA_ID_SUP1_VALID_MASK) == ATA_ID_SUP1_VALID &&
474433d6423SLionel Sambuc !(ps->flags & FLAG_READONLY)) {
475433d6423SLionel Sambuc /* Save write cache related capabilities of the device. It is
476433d6423SLionel Sambuc * possible, although unlikely, that a device has support for
477433d6423SLionel Sambuc * either of these but not both.
478433d6423SLionel Sambuc */
479433d6423SLionel Sambuc if (buf[ATA_ID_SUP0] & ATA_ID_SUP0_WCACHE)
480433d6423SLionel Sambuc ps->flags |= FLAG_HAS_WCACHE;
481433d6423SLionel Sambuc
482433d6423SLionel Sambuc if (buf[ATA_ID_SUP1] & ATA_ID_SUP1_FLUSH)
483433d6423SLionel Sambuc ps->flags |= FLAG_HAS_FLUSH;
484433d6423SLionel Sambuc }
485433d6423SLionel Sambuc
486433d6423SLionel Sambuc return TRUE;
487433d6423SLionel Sambuc }
488433d6423SLionel Sambuc
489433d6423SLionel Sambuc /*===========================================================================*
490433d6423SLionel Sambuc * atapi_transfer *
491433d6423SLionel Sambuc *===========================================================================*/
atapi_transfer(struct port_state * ps,int cmd,u64_t start_lba,unsigned int count,int write,prd_t * prdt,int nr_prds)492433d6423SLionel Sambuc static int atapi_transfer(struct port_state *ps, int cmd, u64_t start_lba,
493433d6423SLionel Sambuc unsigned int count, int write, prd_t *prdt, int nr_prds)
494433d6423SLionel Sambuc {
495433d6423SLionel Sambuc /* Perform data transfer from or to an ATAPI device.
496433d6423SLionel Sambuc */
497433d6423SLionel Sambuc cmd_fis_t fis;
498433d6423SLionel Sambuc u8_t packet[ATAPI_PACKET_SIZE];
499433d6423SLionel Sambuc
500433d6423SLionel Sambuc /* Fill in a Register Host to Device FIS. */
501433d6423SLionel Sambuc memset(&fis, 0, sizeof(fis));
502433d6423SLionel Sambuc fis.cf_cmd = ATA_CMD_PACKET;
503433d6423SLionel Sambuc fis.cf_feat = ATA_FEAT_PACKET_DMA;
504433d6423SLionel Sambuc if (!write && (ps->flags & FLAG_USE_DMADIR))
505433d6423SLionel Sambuc fis.cf_feat |= ATA_FEAT_PACKET_DMADIR;
506433d6423SLionel Sambuc
507433d6423SLionel Sambuc /* Fill in a packet. */
508433d6423SLionel Sambuc memset(packet, 0, sizeof(packet));
509433d6423SLionel Sambuc packet[0] = write ? ATAPI_CMD_WRITE : ATAPI_CMD_READ;
510433d6423SLionel Sambuc packet[2] = (start_lba >> 24) & 0xFF;
511433d6423SLionel Sambuc packet[3] = (start_lba >> 16) & 0xFF;
512433d6423SLionel Sambuc packet[4] = (start_lba >> 8) & 0xFF;
513433d6423SLionel Sambuc packet[5] = start_lba & 0xFF;
514433d6423SLionel Sambuc packet[6] = (count >> 24) & 0xFF;
515433d6423SLionel Sambuc packet[7] = (count >> 16) & 0xFF;
516433d6423SLionel Sambuc packet[8] = (count >> 8) & 0xFF;
517433d6423SLionel Sambuc packet[9] = count & 0xFF;
518433d6423SLionel Sambuc
519433d6423SLionel Sambuc /* Start the command, and wait for it to complete or fail. */
520433d6423SLionel Sambuc port_set_cmd(ps, cmd, &fis, packet, prdt, nr_prds, write);
521433d6423SLionel Sambuc
522433d6423SLionel Sambuc return port_exec(ps, cmd, ahci_transfer_timeout);
523433d6423SLionel Sambuc }
524433d6423SLionel Sambuc
525433d6423SLionel Sambuc /*===========================================================================*
526433d6423SLionel Sambuc * ata_id_check *
527433d6423SLionel Sambuc *===========================================================================*/
ata_id_check(struct port_state * ps,u16_t * buf)528433d6423SLionel Sambuc static int ata_id_check(struct port_state *ps, u16_t *buf)
529433d6423SLionel Sambuc {
530433d6423SLionel Sambuc /* Determine whether we support this ATA device based on the
531433d6423SLionel Sambuc * identification data it returned, and store some of its properties.
532433d6423SLionel Sambuc */
533433d6423SLionel Sambuc
534433d6423SLionel Sambuc /* This must be an ATA device; it must not have removable media;
535433d6423SLionel Sambuc * it must support LBA and DMA; it must support the FLUSH CACHE
536433d6423SLionel Sambuc * command; it must support 48-bit addressing.
537433d6423SLionel Sambuc */
538433d6423SLionel Sambuc if ((buf[ATA_ID_GCAP] & (ATA_ID_GCAP_ATA_MASK | ATA_ID_GCAP_REMOVABLE |
539433d6423SLionel Sambuc ATA_ID_GCAP_INCOMPLETE)) != ATA_ID_GCAP_ATA ||
540433d6423SLionel Sambuc (buf[ATA_ID_CAP] & (ATA_ID_CAP_LBA | ATA_ID_CAP_DMA)) !=
541433d6423SLionel Sambuc (ATA_ID_CAP_LBA | ATA_ID_CAP_DMA) ||
542433d6423SLionel Sambuc (buf[ATA_ID_SUP1] & (ATA_ID_SUP1_VALID_MASK |
543433d6423SLionel Sambuc ATA_ID_SUP1_FLUSH | ATA_ID_SUP1_LBA48)) !=
544433d6423SLionel Sambuc (ATA_ID_SUP1_VALID | ATA_ID_SUP1_FLUSH | ATA_ID_SUP1_LBA48)) {
545433d6423SLionel Sambuc
546433d6423SLionel Sambuc dprintf(V_ERR, ("%s: unsupported ATA device\n",
547433d6423SLionel Sambuc ahci_portname(ps)));
548433d6423SLionel Sambuc
549433d6423SLionel Sambuc dprintf(V_DEV, ("%s: GCAP %04x CAP %04x SUP1 %04x\n",
550433d6423SLionel Sambuc ahci_portname(ps), buf[ATA_ID_GCAP], buf[ATA_ID_CAP],
551433d6423SLionel Sambuc buf[ATA_ID_SUP1]));
552433d6423SLionel Sambuc
553433d6423SLionel Sambuc return FALSE;
554433d6423SLionel Sambuc }
555433d6423SLionel Sambuc
556433d6423SLionel Sambuc /* Get number of LBA blocks, and sector size. */
557433d6423SLionel Sambuc ps->lba_count = ((u64_t) buf[ATA_ID_LBA3] << 48) |
558433d6423SLionel Sambuc ((u64_t) buf[ATA_ID_LBA2] << 32) |
559433d6423SLionel Sambuc ((u64_t) buf[ATA_ID_LBA1] << 16) |
560433d6423SLionel Sambuc (u64_t) buf[ATA_ID_LBA0];
561433d6423SLionel Sambuc
562433d6423SLionel Sambuc /* Determine the queue depth of the device. */
563433d6423SLionel Sambuc if (hba_state.has_ncq &&
564433d6423SLionel Sambuc (buf[ATA_ID_SATA_CAP] & ATA_ID_SATA_CAP_NCQ)) {
565433d6423SLionel Sambuc ps->flags |= FLAG_HAS_NCQ;
566433d6423SLionel Sambuc ps->queue_depth =
567433d6423SLionel Sambuc (buf[ATA_ID_QDEPTH] & ATA_ID_QDEPTH_MASK) + 1;
568433d6423SLionel Sambuc if (ps->queue_depth > hba_state.nr_cmds)
569433d6423SLionel Sambuc ps->queue_depth = hba_state.nr_cmds;
570433d6423SLionel Sambuc }
571433d6423SLionel Sambuc
572433d6423SLionel Sambuc /* For now, we only support long logical sectors. Long physical sector
573433d6423SLionel Sambuc * support may be added later. Note that the given value is in words.
574433d6423SLionel Sambuc */
575433d6423SLionel Sambuc if ((buf[ATA_ID_PLSS] & (ATA_ID_PLSS_VALID_MASK | ATA_ID_PLSS_LLS)) ==
576433d6423SLionel Sambuc (ATA_ID_PLSS_VALID | ATA_ID_PLSS_LLS))
577433d6423SLionel Sambuc ps->sector_size =
578433d6423SLionel Sambuc ((buf[ATA_ID_LSS1] << 16) | buf[ATA_ID_LSS0]) << 1;
579433d6423SLionel Sambuc else
580433d6423SLionel Sambuc ps->sector_size = ATA_SECTOR_SIZE;
581433d6423SLionel Sambuc
582433d6423SLionel Sambuc if (ps->sector_size < ATA_SECTOR_SIZE) {
583433d6423SLionel Sambuc dprintf(V_ERR, ("%s: invalid sector size %u\n",
584433d6423SLionel Sambuc ahci_portname(ps), ps->sector_size));
585433d6423SLionel Sambuc
586433d6423SLionel Sambuc return FALSE;
587433d6423SLionel Sambuc }
588433d6423SLionel Sambuc
589433d6423SLionel Sambuc ps->flags |= FLAG_HAS_MEDIUM | FLAG_HAS_FLUSH;
590433d6423SLionel Sambuc
591433d6423SLionel Sambuc /* FLUSH CACHE is mandatory for ATA devices; write caches are not. */
592433d6423SLionel Sambuc if (buf[ATA_ID_SUP0] & ATA_ID_SUP0_WCACHE)
593433d6423SLionel Sambuc ps->flags |= FLAG_HAS_WCACHE;
594433d6423SLionel Sambuc
595433d6423SLionel Sambuc /* Check Force Unit Access capability of the device. */
596433d6423SLionel Sambuc if ((buf[ATA_ID_ENA2] & (ATA_ID_ENA2_VALID_MASK | ATA_ID_ENA2_FUA)) ==
597433d6423SLionel Sambuc (ATA_ID_ENA2_VALID | ATA_ID_ENA2_FUA))
598433d6423SLionel Sambuc ps->flags |= FLAG_HAS_FUA;
599433d6423SLionel Sambuc
600433d6423SLionel Sambuc return TRUE;
601433d6423SLionel Sambuc }
602433d6423SLionel Sambuc
603433d6423SLionel Sambuc /*===========================================================================*
604433d6423SLionel Sambuc * ata_transfer *
605433d6423SLionel Sambuc *===========================================================================*/
ata_transfer(struct port_state * ps,int cmd,u64_t start_lba,unsigned int count,int write,int force,prd_t * prdt,int nr_prds)606433d6423SLionel Sambuc static int ata_transfer(struct port_state *ps, int cmd, u64_t start_lba,
607433d6423SLionel Sambuc unsigned int count, int write, int force, prd_t *prdt, int nr_prds)
608433d6423SLionel Sambuc {
609433d6423SLionel Sambuc /* Perform data transfer from or to an ATA device.
610433d6423SLionel Sambuc */
611433d6423SLionel Sambuc cmd_fis_t fis;
612433d6423SLionel Sambuc
613433d6423SLionel Sambuc assert(count <= ATA_MAX_SECTORS);
614433d6423SLionel Sambuc
615433d6423SLionel Sambuc /* Special case for sector counts: 65536 is specified as 0. */
616433d6423SLionel Sambuc if (count == ATA_MAX_SECTORS)
617433d6423SLionel Sambuc count = 0;
618433d6423SLionel Sambuc
619433d6423SLionel Sambuc memset(&fis, 0, sizeof(fis));
620433d6423SLionel Sambuc fis.cf_dev = ATA_DEV_LBA;
621433d6423SLionel Sambuc if (ps->flags & FLAG_HAS_NCQ) {
622433d6423SLionel Sambuc if (write) {
623433d6423SLionel Sambuc if (force && (ps->flags & FLAG_HAS_FUA))
624433d6423SLionel Sambuc fis.cf_dev |= ATA_DEV_FUA;
625433d6423SLionel Sambuc
626433d6423SLionel Sambuc fis.cf_cmd = ATA_CMD_WRITE_FPDMA_QUEUED;
627433d6423SLionel Sambuc } else {
628433d6423SLionel Sambuc fis.cf_cmd = ATA_CMD_READ_FPDMA_QUEUED;
629433d6423SLionel Sambuc }
630433d6423SLionel Sambuc }
631433d6423SLionel Sambuc else {
632433d6423SLionel Sambuc if (write) {
633433d6423SLionel Sambuc if (force && (ps->flags & FLAG_HAS_FUA))
634433d6423SLionel Sambuc fis.cf_cmd = ATA_CMD_WRITE_DMA_FUA_EXT;
635433d6423SLionel Sambuc else
636433d6423SLionel Sambuc fis.cf_cmd = ATA_CMD_WRITE_DMA_EXT;
637433d6423SLionel Sambuc }
638433d6423SLionel Sambuc else {
639433d6423SLionel Sambuc fis.cf_cmd = ATA_CMD_READ_DMA_EXT;
640433d6423SLionel Sambuc }
641433d6423SLionel Sambuc }
642433d6423SLionel Sambuc fis.cf_lba = start_lba & 0x00FFFFFFUL;
643433d6423SLionel Sambuc fis.cf_lba_exp = (start_lba >> 24) & 0x00FFFFFFUL;
644433d6423SLionel Sambuc fis.cf_sec = count & 0xFF;
645433d6423SLionel Sambuc fis.cf_sec_exp = (count >> 8) & 0xFF;
646433d6423SLionel Sambuc
647433d6423SLionel Sambuc /* Start the command, and wait for it to complete or fail. */
648433d6423SLionel Sambuc port_set_cmd(ps, cmd, &fis, NULL /*packet*/, prdt, nr_prds, write);
649433d6423SLionel Sambuc
650433d6423SLionel Sambuc return port_exec(ps, cmd, ahci_transfer_timeout);
651433d6423SLionel Sambuc }
652433d6423SLionel Sambuc
653433d6423SLionel Sambuc /*===========================================================================*
654433d6423SLionel Sambuc * gen_identify *
655433d6423SLionel Sambuc *===========================================================================*/
gen_identify(struct port_state * ps,int blocking)656433d6423SLionel Sambuc static int gen_identify(struct port_state *ps, int blocking)
657433d6423SLionel Sambuc {
658433d6423SLionel Sambuc /* Identify an ATA or ATAPI device. If the blocking flag is set, block
659433d6423SLionel Sambuc * until the command has completed; otherwise return immediately.
660433d6423SLionel Sambuc */
661433d6423SLionel Sambuc cmd_fis_t fis;
662433d6423SLionel Sambuc prd_t prd;
663433d6423SLionel Sambuc
664433d6423SLionel Sambuc /* Set up a command, and a single PRD for the result. */
665433d6423SLionel Sambuc memset(&fis, 0, sizeof(fis));
666433d6423SLionel Sambuc
667433d6423SLionel Sambuc if (ps->flags & FLAG_ATAPI)
668433d6423SLionel Sambuc fis.cf_cmd = ATA_CMD_IDENTIFY_PACKET;
669433d6423SLionel Sambuc else
670433d6423SLionel Sambuc fis.cf_cmd = ATA_CMD_IDENTIFY;
671433d6423SLionel Sambuc
672433d6423SLionel Sambuc prd.vp_addr = ps->tmp_phys;
673433d6423SLionel Sambuc prd.vp_size = ATA_ID_SIZE;
674433d6423SLionel Sambuc
675433d6423SLionel Sambuc /* Start the command, and possibly wait for the result. */
676433d6423SLionel Sambuc port_set_cmd(ps, 0, &fis, NULL /*packet*/, &prd, 1, FALSE /*write*/);
677433d6423SLionel Sambuc
678433d6423SLionel Sambuc if (blocking)
679433d6423SLionel Sambuc return port_exec(ps, 0, ahci_command_timeout);
680433d6423SLionel Sambuc
681433d6423SLionel Sambuc port_issue(ps, 0, ahci_command_timeout);
682433d6423SLionel Sambuc
683433d6423SLionel Sambuc return OK;
684433d6423SLionel Sambuc }
685433d6423SLionel Sambuc
686433d6423SLionel Sambuc /*===========================================================================*
687433d6423SLionel Sambuc * gen_flush_wcache *
688433d6423SLionel Sambuc *===========================================================================*/
gen_flush_wcache(struct port_state * ps)689433d6423SLionel Sambuc static int gen_flush_wcache(struct port_state *ps)
690433d6423SLionel Sambuc {
691433d6423SLionel Sambuc /* Flush the device's write cache.
692433d6423SLionel Sambuc */
693433d6423SLionel Sambuc cmd_fis_t fis;
694433d6423SLionel Sambuc
695433d6423SLionel Sambuc /* The FLUSH CACHE command may not be supported by all (writable ATAPI)
696433d6423SLionel Sambuc * devices.
697433d6423SLionel Sambuc */
698433d6423SLionel Sambuc if (!(ps->flags & FLAG_HAS_FLUSH))
699433d6423SLionel Sambuc return EINVAL;
700433d6423SLionel Sambuc
701433d6423SLionel Sambuc /* Use the FLUSH CACHE command for both ATA and ATAPI. We are not
702433d6423SLionel Sambuc * interested in the disk location of a failure, so there is no reason
703433d6423SLionel Sambuc * to use the ATA-only FLUSH CACHE EXT command. Either way, the command
704433d6423SLionel Sambuc * may indeed fail due to a disk error, in which case it should be
705433d6423SLionel Sambuc * repeated. For now, we shift this responsibility onto the caller.
706433d6423SLionel Sambuc */
707433d6423SLionel Sambuc memset(&fis, 0, sizeof(fis));
708433d6423SLionel Sambuc fis.cf_cmd = ATA_CMD_FLUSH_CACHE;
709433d6423SLionel Sambuc
710433d6423SLionel Sambuc /* Start the command, and wait for it to complete or fail.
711433d6423SLionel Sambuc * The flush command may take longer than regular I/O commands.
712433d6423SLionel Sambuc */
713433d6423SLionel Sambuc port_set_cmd(ps, 0, &fis, NULL /*packet*/, NULL /*prdt*/, 0,
714433d6423SLionel Sambuc FALSE /*write*/);
715433d6423SLionel Sambuc
716433d6423SLionel Sambuc return port_exec(ps, 0, ahci_flush_timeout);
717433d6423SLionel Sambuc }
718433d6423SLionel Sambuc
719433d6423SLionel Sambuc /*===========================================================================*
720433d6423SLionel Sambuc * gen_get_wcache *
721433d6423SLionel Sambuc *===========================================================================*/
gen_get_wcache(struct port_state * ps,int * val)722433d6423SLionel Sambuc static int gen_get_wcache(struct port_state *ps, int *val)
723433d6423SLionel Sambuc {
724433d6423SLionel Sambuc /* Retrieve the status of the device's write cache.
725433d6423SLionel Sambuc */
726433d6423SLionel Sambuc int r;
727433d6423SLionel Sambuc
728433d6423SLionel Sambuc /* Write caches are not mandatory. */
729433d6423SLionel Sambuc if (!(ps->flags & FLAG_HAS_WCACHE))
730433d6423SLionel Sambuc return EINVAL;
731433d6423SLionel Sambuc
732433d6423SLionel Sambuc /* Retrieve information about the device. */
733433d6423SLionel Sambuc if ((r = gen_identify(ps, TRUE /*blocking*/)) != OK)
734433d6423SLionel Sambuc return r;
735433d6423SLionel Sambuc
736433d6423SLionel Sambuc /* Return the current setting. */
737433d6423SLionel Sambuc *val = !!(((u16_t *) ps->tmp_base)[ATA_ID_ENA0] & ATA_ID_ENA0_WCACHE);
738433d6423SLionel Sambuc
739433d6423SLionel Sambuc return OK;
740433d6423SLionel Sambuc }
741433d6423SLionel Sambuc
742433d6423SLionel Sambuc /*===========================================================================*
743433d6423SLionel Sambuc * gen_set_wcache *
744433d6423SLionel Sambuc *===========================================================================*/
gen_set_wcache(struct port_state * ps,int enable)745433d6423SLionel Sambuc static int gen_set_wcache(struct port_state *ps, int enable)
746433d6423SLionel Sambuc {
747433d6423SLionel Sambuc /* Enable or disable the device's write cache.
748433d6423SLionel Sambuc */
749433d6423SLionel Sambuc cmd_fis_t fis;
750433d6423SLionel Sambuc clock_t timeout;
751433d6423SLionel Sambuc
752433d6423SLionel Sambuc /* Write caches are not mandatory. */
753433d6423SLionel Sambuc if (!(ps->flags & FLAG_HAS_WCACHE))
754433d6423SLionel Sambuc return EINVAL;
755433d6423SLionel Sambuc
756433d6423SLionel Sambuc /* Disabling the write cache causes a (blocking) cache flush. Cache
757433d6423SLionel Sambuc * flushes may take much longer than regular commands.
758433d6423SLionel Sambuc */
759433d6423SLionel Sambuc timeout = enable ? ahci_command_timeout : ahci_flush_timeout;
760433d6423SLionel Sambuc
761433d6423SLionel Sambuc /* Set up a command. */
762433d6423SLionel Sambuc memset(&fis, 0, sizeof(fis));
763433d6423SLionel Sambuc fis.cf_cmd = ATA_CMD_SET_FEATURES;
764433d6423SLionel Sambuc fis.cf_feat = enable ? ATA_SF_EN_WCACHE : ATA_SF_DI_WCACHE;
765433d6423SLionel Sambuc
766433d6423SLionel Sambuc /* Start the command, and wait for it to complete or fail. */
767433d6423SLionel Sambuc port_set_cmd(ps, 0, &fis, NULL /*packet*/, NULL /*prdt*/, 0,
768433d6423SLionel Sambuc FALSE /*write*/);
769433d6423SLionel Sambuc
770433d6423SLionel Sambuc return port_exec(ps, 0, timeout);
771433d6423SLionel Sambuc }
772433d6423SLionel Sambuc
773433d6423SLionel Sambuc /*===========================================================================*
774433d6423SLionel Sambuc * ct_set_fis *
775433d6423SLionel Sambuc *===========================================================================*/
ct_set_fis(u8_t * ct,cmd_fis_t * fis,unsigned int tag)776433d6423SLionel Sambuc static vir_bytes ct_set_fis(u8_t *ct, cmd_fis_t *fis, unsigned int tag)
777433d6423SLionel Sambuc {
778433d6423SLionel Sambuc /* Fill in the Frame Information Structure part of a command table,
779433d6423SLionel Sambuc * and return the resulting FIS size (in bytes). We only support the
780433d6423SLionel Sambuc * command Register - Host to Device FIS type.
781433d6423SLionel Sambuc */
782433d6423SLionel Sambuc
783433d6423SLionel Sambuc memset(ct, 0, ATA_H2D_SIZE);
784433d6423SLionel Sambuc ct[ATA_FIS_TYPE] = ATA_FIS_TYPE_H2D;
785433d6423SLionel Sambuc ct[ATA_H2D_FLAGS] = ATA_H2D_FLAGS_C;
786433d6423SLionel Sambuc ct[ATA_H2D_CMD] = fis->cf_cmd;
787433d6423SLionel Sambuc ct[ATA_H2D_LBA_LOW] = fis->cf_lba & 0xFF;
788433d6423SLionel Sambuc ct[ATA_H2D_LBA_MID] = (fis->cf_lba >> 8) & 0xFF;
789433d6423SLionel Sambuc ct[ATA_H2D_LBA_HIGH] = (fis->cf_lba >> 16) & 0xFF;
790433d6423SLionel Sambuc ct[ATA_H2D_DEV] = fis->cf_dev;
791433d6423SLionel Sambuc ct[ATA_H2D_LBA_LOW_EXP] = fis->cf_lba_exp & 0xFF;
792433d6423SLionel Sambuc ct[ATA_H2D_LBA_MID_EXP] = (fis->cf_lba_exp >> 8) & 0xFF;
793433d6423SLionel Sambuc ct[ATA_H2D_LBA_HIGH_EXP] = (fis->cf_lba_exp >> 16) & 0xFF;
794433d6423SLionel Sambuc ct[ATA_H2D_CTL] = fis->cf_ctl;
795433d6423SLionel Sambuc
796433d6423SLionel Sambuc if (ATA_IS_FPDMA_CMD(fis->cf_cmd)) {
797433d6423SLionel Sambuc ct[ATA_H2D_FEAT] = fis->cf_sec;
798433d6423SLionel Sambuc ct[ATA_H2D_FEAT_EXP] = fis->cf_sec_exp;
799433d6423SLionel Sambuc ct[ATA_H2D_SEC] = tag << ATA_SEC_TAG_SHIFT;
800433d6423SLionel Sambuc ct[ATA_H2D_SEC_EXP] = 0;
801433d6423SLionel Sambuc } else {
802433d6423SLionel Sambuc ct[ATA_H2D_FEAT] = fis->cf_feat;
803433d6423SLionel Sambuc ct[ATA_H2D_FEAT_EXP] = fis->cf_feat_exp;
804433d6423SLionel Sambuc ct[ATA_H2D_SEC] = fis->cf_sec;
805433d6423SLionel Sambuc ct[ATA_H2D_SEC_EXP] = fis->cf_sec_exp;
806433d6423SLionel Sambuc }
807433d6423SLionel Sambuc
808433d6423SLionel Sambuc return ATA_H2D_SIZE;
809433d6423SLionel Sambuc }
810433d6423SLionel Sambuc
811433d6423SLionel Sambuc /*===========================================================================*
812433d6423SLionel Sambuc * ct_set_packet *
813433d6423SLionel Sambuc *===========================================================================*/
ct_set_packet(u8_t * ct,u8_t packet[ATAPI_PACKET_SIZE])814433d6423SLionel Sambuc static void ct_set_packet(u8_t *ct, u8_t packet[ATAPI_PACKET_SIZE])
815433d6423SLionel Sambuc {
816433d6423SLionel Sambuc /* Fill in the packet part of a command table.
817433d6423SLionel Sambuc */
818433d6423SLionel Sambuc
819433d6423SLionel Sambuc memcpy(&ct[AHCI_CT_PACKET_OFF], packet, ATAPI_PACKET_SIZE);
820433d6423SLionel Sambuc }
821433d6423SLionel Sambuc
822433d6423SLionel Sambuc /*===========================================================================*
823433d6423SLionel Sambuc * ct_set_prdt *
824433d6423SLionel Sambuc *===========================================================================*/
ct_set_prdt(u8_t * ct,prd_t * prdt,int nr_prds)825433d6423SLionel Sambuc static void ct_set_prdt(u8_t *ct, prd_t *prdt, int nr_prds)
826433d6423SLionel Sambuc {
827433d6423SLionel Sambuc /* Fill in the PRDT part of a command table.
828433d6423SLionel Sambuc */
829433d6423SLionel Sambuc u32_t *p;
830433d6423SLionel Sambuc int i;
831433d6423SLionel Sambuc
832433d6423SLionel Sambuc p = (u32_t *) &ct[AHCI_CT_PRDT_OFF];
833433d6423SLionel Sambuc
834433d6423SLionel Sambuc for (i = 0; i < nr_prds; i++, prdt++) {
835433d6423SLionel Sambuc *p++ = prdt->vp_addr;
836433d6423SLionel Sambuc *p++ = 0;
837433d6423SLionel Sambuc *p++ = 0;
838433d6423SLionel Sambuc *p++ = prdt->vp_size - 1;
839433d6423SLionel Sambuc }
840433d6423SLionel Sambuc }
841433d6423SLionel Sambuc
842433d6423SLionel Sambuc /*===========================================================================*
843433d6423SLionel Sambuc * port_set_cmd *
844433d6423SLionel Sambuc *===========================================================================*/
port_set_cmd(struct port_state * ps,int cmd,cmd_fis_t * fis,u8_t packet[ATAPI_PACKET_SIZE],prd_t * prdt,int nr_prds,int write)845433d6423SLionel Sambuc static void port_set_cmd(struct port_state *ps, int cmd, cmd_fis_t *fis,
846433d6423SLionel Sambuc u8_t packet[ATAPI_PACKET_SIZE], prd_t *prdt, int nr_prds, int write)
847433d6423SLionel Sambuc {
848433d6423SLionel Sambuc /* Prepare the given command for execution, by constructing a command
849433d6423SLionel Sambuc * table and setting up a command list entry pointing to the table.
850433d6423SLionel Sambuc */
851433d6423SLionel Sambuc u8_t *ct;
852433d6423SLionel Sambuc u32_t *cl;
853433d6423SLionel Sambuc vir_bytes size;
854433d6423SLionel Sambuc
855433d6423SLionel Sambuc /* Set a port-specific flag that tells us if the command being
856433d6423SLionel Sambuc * processed is a NCQ command or not.
857433d6423SLionel Sambuc */
858433d6423SLionel Sambuc if (ATA_IS_FPDMA_CMD(fis->cf_cmd)) {
859433d6423SLionel Sambuc ps->flags |= FLAG_NCQ_MODE;
860433d6423SLionel Sambuc } else {
861433d6423SLionel Sambuc assert(!ps->pend_mask);
862433d6423SLionel Sambuc ps->flags &= ~FLAG_NCQ_MODE;
863433d6423SLionel Sambuc }
864433d6423SLionel Sambuc
865433d6423SLionel Sambuc /* Construct a command table, consisting of a command FIS, optionally
866433d6423SLionel Sambuc * a packet, and optionally a number of PRDs (making up the actual PRD
867433d6423SLionel Sambuc * table).
868433d6423SLionel Sambuc */
869433d6423SLionel Sambuc ct = ps->ct_base[cmd];
870433d6423SLionel Sambuc
871433d6423SLionel Sambuc assert(ct != NULL);
872433d6423SLionel Sambuc assert(nr_prds <= NR_PRDS);
873433d6423SLionel Sambuc
874433d6423SLionel Sambuc size = ct_set_fis(ct, fis, cmd);
875433d6423SLionel Sambuc
876433d6423SLionel Sambuc if (packet != NULL)
877433d6423SLionel Sambuc ct_set_packet(ct, packet);
878433d6423SLionel Sambuc
879433d6423SLionel Sambuc ct_set_prdt(ct, prdt, nr_prds);
880433d6423SLionel Sambuc
881433d6423SLionel Sambuc /* Construct a command list entry, pointing to the command's table.
882433d6423SLionel Sambuc * Current assumptions: callers always provide a Register - Host to
883433d6423SLionel Sambuc * Device type FIS, and all non-NCQ commands are prefetchable.
884433d6423SLionel Sambuc */
885433d6423SLionel Sambuc cl = &ps->cl_base[cmd * AHCI_CL_ENTRY_DWORDS];
886433d6423SLionel Sambuc
887433d6423SLionel Sambuc memset(cl, 0, AHCI_CL_ENTRY_SIZE);
888433d6423SLionel Sambuc cl[0] = (nr_prds << AHCI_CL_PRDTL_SHIFT) |
889433d6423SLionel Sambuc ((!ATA_IS_FPDMA_CMD(fis->cf_cmd) &&
890433d6423SLionel Sambuc (nr_prds > 0 || packet != NULL)) ? AHCI_CL_PREFETCHABLE : 0) |
891433d6423SLionel Sambuc (write ? AHCI_CL_WRITE : 0) |
892433d6423SLionel Sambuc ((packet != NULL) ? AHCI_CL_ATAPI : 0) |
893433d6423SLionel Sambuc ((size / sizeof(u32_t)) << AHCI_CL_CFL_SHIFT);
894433d6423SLionel Sambuc cl[2] = ps->ct_phys[cmd];
895433d6423SLionel Sambuc }
896433d6423SLionel Sambuc
897433d6423SLionel Sambuc /*===========================================================================*
898433d6423SLionel Sambuc * port_finish_cmd *
899433d6423SLionel Sambuc *===========================================================================*/
port_finish_cmd(struct port_state * ps,int cmd,int result)900433d6423SLionel Sambuc static void port_finish_cmd(struct port_state *ps, int cmd, int result)
901433d6423SLionel Sambuc {
902433d6423SLionel Sambuc /* Finish a command that has either succeeded or failed.
903433d6423SLionel Sambuc */
904433d6423SLionel Sambuc
905433d6423SLionel Sambuc assert(cmd < ps->queue_depth);
906433d6423SLionel Sambuc
907433d6423SLionel Sambuc dprintf(V_REQ, ("%s: command %d %s\n", ahci_portname(ps),
908433d6423SLionel Sambuc cmd, (result == RESULT_SUCCESS) ? "succeeded" : "failed"));
909433d6423SLionel Sambuc
910433d6423SLionel Sambuc /* Update the command result, and clear it from the pending list. */
911433d6423SLionel Sambuc ps->cmd_info[cmd].result = result;
912433d6423SLionel Sambuc
913433d6423SLionel Sambuc assert(ps->pend_mask & (1 << cmd));
914433d6423SLionel Sambuc ps->pend_mask &= ~(1 << cmd);
915433d6423SLionel Sambuc
916433d6423SLionel Sambuc /* Wake up the thread, unless it is the main thread. This can happen
917433d6423SLionel Sambuc * during initialization, as the gen_identify function is called by the
918433d6423SLionel Sambuc * main thread itself.
919433d6423SLionel Sambuc */
920433d6423SLionel Sambuc if (ps->state != STATE_WAIT_ID)
921433d6423SLionel Sambuc blockdriver_mt_wakeup(ps->cmd_info[cmd].tid);
922433d6423SLionel Sambuc }
923433d6423SLionel Sambuc
924433d6423SLionel Sambuc /*===========================================================================*
925433d6423SLionel Sambuc * port_fail_cmds *
926433d6423SLionel Sambuc *===========================================================================*/
port_fail_cmds(struct port_state * ps)927433d6423SLionel Sambuc static void port_fail_cmds(struct port_state *ps)
928433d6423SLionel Sambuc {
929433d6423SLionel Sambuc /* Fail all ongoing commands for a device.
930433d6423SLionel Sambuc */
931433d6423SLionel Sambuc int i;
932433d6423SLionel Sambuc
933433d6423SLionel Sambuc for (i = 0; ps->pend_mask != 0 && i < ps->queue_depth; i++)
934433d6423SLionel Sambuc if (ps->pend_mask & (1 << i))
935433d6423SLionel Sambuc port_finish_cmd(ps, i, RESULT_FAILURE);
936433d6423SLionel Sambuc }
937433d6423SLionel Sambuc
938433d6423SLionel Sambuc /*===========================================================================*
939433d6423SLionel Sambuc * port_check_cmds *
940433d6423SLionel Sambuc *===========================================================================*/
port_check_cmds(struct port_state * ps)941433d6423SLionel Sambuc static void port_check_cmds(struct port_state *ps)
942433d6423SLionel Sambuc {
943433d6423SLionel Sambuc /* Check what commands have completed, and finish them.
944433d6423SLionel Sambuc */
945433d6423SLionel Sambuc u32_t mask, done;
946433d6423SLionel Sambuc int i;
947433d6423SLionel Sambuc
948433d6423SLionel Sambuc /* See which commands have completed. */
949433d6423SLionel Sambuc if (ps->flags & FLAG_NCQ_MODE)
950433d6423SLionel Sambuc mask = port_read(ps, AHCI_PORT_SACT);
951433d6423SLionel Sambuc else
952433d6423SLionel Sambuc mask = port_read(ps, AHCI_PORT_CI);
953433d6423SLionel Sambuc
954433d6423SLionel Sambuc /* Wake up threads corresponding to completed commands. */
955433d6423SLionel Sambuc done = ps->pend_mask & ~mask;
956433d6423SLionel Sambuc
957433d6423SLionel Sambuc for (i = 0; i < ps->queue_depth; i++)
958433d6423SLionel Sambuc if (done & (1 << i))
959433d6423SLionel Sambuc port_finish_cmd(ps, i, RESULT_SUCCESS);
960433d6423SLionel Sambuc }
961433d6423SLionel Sambuc
962433d6423SLionel Sambuc /*===========================================================================*
963433d6423SLionel Sambuc * port_find_cmd *
964433d6423SLionel Sambuc *===========================================================================*/
port_find_cmd(struct port_state * ps)965433d6423SLionel Sambuc static int port_find_cmd(struct port_state *ps)
966433d6423SLionel Sambuc {
967433d6423SLionel Sambuc /* Find a free command tag to queue the current request.
968433d6423SLionel Sambuc */
969433d6423SLionel Sambuc int i;
970433d6423SLionel Sambuc
971433d6423SLionel Sambuc for (i = 0; i < ps->queue_depth; i++)
972433d6423SLionel Sambuc if (!(ps->pend_mask & (1 << i)))
973433d6423SLionel Sambuc break;
974433d6423SLionel Sambuc
975433d6423SLionel Sambuc /* We should always be able to find a free slot, since a thread runs
976433d6423SLionel Sambuc * only when it is free, and thus, only because a slot is available.
977433d6423SLionel Sambuc */
978433d6423SLionel Sambuc assert(i < ps->queue_depth);
979433d6423SLionel Sambuc
980433d6423SLionel Sambuc return i;
981433d6423SLionel Sambuc }
982433d6423SLionel Sambuc
983433d6423SLionel Sambuc /*===========================================================================*
984433d6423SLionel Sambuc * port_get_padbuf *
985433d6423SLionel Sambuc *===========================================================================*/
port_get_padbuf(struct port_state * ps,size_t size)986433d6423SLionel Sambuc static int port_get_padbuf(struct port_state *ps, size_t size)
987433d6423SLionel Sambuc {
988433d6423SLionel Sambuc /* Make available a temporary buffer for use by this port. Enlarge the
989433d6423SLionel Sambuc * previous buffer if applicable and necessary, potentially changing
990433d6423SLionel Sambuc * its physical address.
991433d6423SLionel Sambuc */
992433d6423SLionel Sambuc
993433d6423SLionel Sambuc if (ps->pad_base != NULL && ps->pad_size >= size)
994433d6423SLionel Sambuc return OK;
995433d6423SLionel Sambuc
996433d6423SLionel Sambuc if (ps->pad_base != NULL)
997433d6423SLionel Sambuc free_contig(ps->pad_base, ps->pad_size);
998433d6423SLionel Sambuc
999433d6423SLionel Sambuc ps->pad_size = size;
1000433d6423SLionel Sambuc ps->pad_base = alloc_contig(ps->pad_size, 0, &ps->pad_phys);
1001433d6423SLionel Sambuc
1002433d6423SLionel Sambuc if (ps->pad_base == NULL) {
1003433d6423SLionel Sambuc dprintf(V_ERR, ("%s: unable to allocate a padding buffer of "
1004433d6423SLionel Sambuc "size %lu\n", ahci_portname(ps),
1005433d6423SLionel Sambuc (unsigned long) size));
1006433d6423SLionel Sambuc
1007433d6423SLionel Sambuc return ENOMEM;
1008433d6423SLionel Sambuc }
1009433d6423SLionel Sambuc
1010433d6423SLionel Sambuc dprintf(V_INFO, ("%s: allocated padding buffer of size %lu\n",
1011433d6423SLionel Sambuc ahci_portname(ps), (unsigned long) size));
1012433d6423SLionel Sambuc
1013433d6423SLionel Sambuc return OK;
1014433d6423SLionel Sambuc }
1015433d6423SLionel Sambuc
1016433d6423SLionel Sambuc /*===========================================================================*
1017433d6423SLionel Sambuc * sum_iovec *
1018433d6423SLionel Sambuc *===========================================================================*/
sum_iovec(struct port_state * ps,endpoint_t endpt,iovec_s_t * iovec,int nr_req,vir_bytes * total)1019433d6423SLionel Sambuc static int sum_iovec(struct port_state *ps, endpoint_t endpt,
1020433d6423SLionel Sambuc iovec_s_t *iovec, int nr_req, vir_bytes *total)
1021433d6423SLionel Sambuc {
1022433d6423SLionel Sambuc /* Retrieve the total size of the given I/O vector. Check for alignment
1023433d6423SLionel Sambuc * requirements along the way. Return OK (and the total request size)
1024433d6423SLionel Sambuc * or an error.
1025433d6423SLionel Sambuc */
1026433d6423SLionel Sambuc vir_bytes size, bytes;
1027433d6423SLionel Sambuc int i;
1028433d6423SLionel Sambuc
1029433d6423SLionel Sambuc bytes = 0;
1030433d6423SLionel Sambuc
1031433d6423SLionel Sambuc for (i = 0; i < nr_req; i++) {
1032433d6423SLionel Sambuc size = iovec[i].iov_size;
1033433d6423SLionel Sambuc
1034433d6423SLionel Sambuc if (size == 0 || (size & 1) || size > LONG_MAX) {
1035433d6423SLionel Sambuc dprintf(V_ERR, ("%s: bad size %lu in iovec from %d\n",
1036433d6423SLionel Sambuc ahci_portname(ps), size, endpt));
1037433d6423SLionel Sambuc return EINVAL;
1038433d6423SLionel Sambuc }
1039433d6423SLionel Sambuc
1040433d6423SLionel Sambuc bytes += size;
1041433d6423SLionel Sambuc
1042433d6423SLionel Sambuc if (bytes > LONG_MAX) {
1043433d6423SLionel Sambuc dprintf(V_ERR, ("%s: iovec size overflow from %d\n",
1044433d6423SLionel Sambuc ahci_portname(ps), endpt));
1045433d6423SLionel Sambuc return EINVAL;
1046433d6423SLionel Sambuc }
1047433d6423SLionel Sambuc }
1048433d6423SLionel Sambuc
1049433d6423SLionel Sambuc *total = bytes;
1050433d6423SLionel Sambuc return OK;
1051433d6423SLionel Sambuc }
1052433d6423SLionel Sambuc
1053433d6423SLionel Sambuc /*===========================================================================*
1054433d6423SLionel Sambuc * setup_prdt *
1055433d6423SLionel Sambuc *===========================================================================*/
setup_prdt(struct port_state * ps,endpoint_t endpt,iovec_s_t * iovec,int nr_req,vir_bytes size,vir_bytes lead,int write,prd_t * prdt)1056433d6423SLionel Sambuc static int setup_prdt(struct port_state *ps, endpoint_t endpt,
1057433d6423SLionel Sambuc iovec_s_t *iovec, int nr_req, vir_bytes size, vir_bytes lead,
1058433d6423SLionel Sambuc int write, prd_t *prdt)
1059433d6423SLionel Sambuc {
1060433d6423SLionel Sambuc /* Convert (the first part of) an I/O vector to a Physical Region
1061433d6423SLionel Sambuc * Descriptor Table describing array that can later be used to set the
1062433d6423SLionel Sambuc * command's real PRDT. The resulting table as a whole should be
1063433d6423SLionel Sambuc * sector-aligned; leading and trailing local buffers may have to be
1064433d6423SLionel Sambuc * used for padding as appropriate. Return the number of PRD entries,
1065433d6423SLionel Sambuc * or a negative error code.
1066433d6423SLionel Sambuc */
1067433d6423SLionel Sambuc struct vumap_vir vvec[NR_PRDS];
1068433d6423SLionel Sambuc size_t bytes, trail;
1069433d6423SLionel Sambuc int i, r, pcount, nr_prds = 0;
1070433d6423SLionel Sambuc
1071433d6423SLionel Sambuc if (lead > 0) {
1072433d6423SLionel Sambuc /* Allocate a buffer for the data we don't want. */
1073433d6423SLionel Sambuc if ((r = port_get_padbuf(ps, ps->sector_size)) != OK)
1074433d6423SLionel Sambuc return r;
1075433d6423SLionel Sambuc
1076433d6423SLionel Sambuc prdt[nr_prds].vp_addr = ps->pad_phys;
1077433d6423SLionel Sambuc prdt[nr_prds].vp_size = lead;
1078433d6423SLionel Sambuc nr_prds++;
1079433d6423SLionel Sambuc }
1080433d6423SLionel Sambuc
1081433d6423SLionel Sambuc /* The sum of lead, size, trail has to be sector-aligned. */
1082433d6423SLionel Sambuc trail = (ps->sector_size - (lead + size)) % ps->sector_size;
1083433d6423SLionel Sambuc
1084433d6423SLionel Sambuc /* Get the physical addresses of the given buffers. */
1085433d6423SLionel Sambuc for (i = 0; i < nr_req && size > 0; i++) {
1086433d6423SLionel Sambuc bytes = MIN(iovec[i].iov_size, size);
1087433d6423SLionel Sambuc
1088433d6423SLionel Sambuc if (endpt == SELF)
1089433d6423SLionel Sambuc vvec[i].vv_addr = (vir_bytes) iovec[i].iov_grant;
1090433d6423SLionel Sambuc else
1091433d6423SLionel Sambuc vvec[i].vv_grant = iovec[i].iov_grant;
1092433d6423SLionel Sambuc
1093433d6423SLionel Sambuc vvec[i].vv_size = bytes;
1094433d6423SLionel Sambuc
1095433d6423SLionel Sambuc size -= bytes;
1096433d6423SLionel Sambuc }
1097433d6423SLionel Sambuc
1098433d6423SLionel Sambuc pcount = i;
1099433d6423SLionel Sambuc
1100433d6423SLionel Sambuc if ((r = sys_vumap(endpt, vvec, i, 0, write ? VUA_READ : VUA_WRITE,
1101433d6423SLionel Sambuc &prdt[nr_prds], &pcount)) != OK) {
1102433d6423SLionel Sambuc dprintf(V_ERR, ("%s: unable to map memory from %d (%d)\n",
1103433d6423SLionel Sambuc ahci_portname(ps), endpt, r));
1104433d6423SLionel Sambuc return r;
1105433d6423SLionel Sambuc }
1106433d6423SLionel Sambuc
1107433d6423SLionel Sambuc assert(pcount > 0 && pcount <= i);
1108433d6423SLionel Sambuc
1109433d6423SLionel Sambuc /* Make sure all buffers are physically contiguous and word-aligned. */
1110433d6423SLionel Sambuc for (i = 0; i < pcount; i++) {
1111433d6423SLionel Sambuc if (vvec[i].vv_size != prdt[nr_prds].vp_size) {
1112433d6423SLionel Sambuc dprintf(V_ERR, ("%s: non-contiguous memory from %d\n",
1113433d6423SLionel Sambuc ahci_portname(ps), endpt));
1114433d6423SLionel Sambuc return EINVAL;
1115433d6423SLionel Sambuc }
1116433d6423SLionel Sambuc
1117433d6423SLionel Sambuc if (prdt[nr_prds].vp_addr & 1) {
1118433d6423SLionel Sambuc dprintf(V_ERR, ("%s: bad physical address from %d\n",
1119433d6423SLionel Sambuc ahci_portname(ps), endpt));
1120433d6423SLionel Sambuc return EINVAL;
1121433d6423SLionel Sambuc }
1122433d6423SLionel Sambuc
1123433d6423SLionel Sambuc nr_prds++;
1124433d6423SLionel Sambuc }
1125433d6423SLionel Sambuc
1126433d6423SLionel Sambuc if (trail > 0) {
1127433d6423SLionel Sambuc assert(nr_prds < NR_PRDS);
1128433d6423SLionel Sambuc prdt[nr_prds].vp_addr = ps->pad_phys + lead;
1129433d6423SLionel Sambuc prdt[nr_prds].vp_size = trail;
1130433d6423SLionel Sambuc nr_prds++;
1131433d6423SLionel Sambuc }
1132433d6423SLionel Sambuc
1133433d6423SLionel Sambuc return nr_prds;
1134433d6423SLionel Sambuc }
1135433d6423SLionel Sambuc
1136433d6423SLionel Sambuc /*===========================================================================*
1137433d6423SLionel Sambuc * port_transfer *
1138433d6423SLionel Sambuc *===========================================================================*/
port_transfer(struct port_state * ps,u64_t pos,u64_t eof,endpoint_t endpt,iovec_s_t * iovec,int nr_req,int write,int flags)1139433d6423SLionel Sambuc static ssize_t port_transfer(struct port_state *ps, u64_t pos, u64_t eof,
1140433d6423SLionel Sambuc endpoint_t endpt, iovec_s_t *iovec, int nr_req, int write, int flags)
1141433d6423SLionel Sambuc {
1142433d6423SLionel Sambuc /* Perform an I/O transfer on a port.
1143433d6423SLionel Sambuc */
1144433d6423SLionel Sambuc prd_t prdt[NR_PRDS];
1145433d6423SLionel Sambuc vir_bytes size, lead;
1146433d6423SLionel Sambuc unsigned int count, nr_prds;
1147433d6423SLionel Sambuc u64_t start_lba;
1148433d6423SLionel Sambuc int r, cmd;
1149433d6423SLionel Sambuc
1150433d6423SLionel Sambuc /* Get the total request size from the I/O vector. */
1151433d6423SLionel Sambuc if ((r = sum_iovec(ps, endpt, iovec, nr_req, &size)) != OK)
1152433d6423SLionel Sambuc return r;
1153433d6423SLionel Sambuc
1154433d6423SLionel Sambuc dprintf(V_REQ, ("%s: %s for %lu bytes at pos %llx\n",
1155433d6423SLionel Sambuc ahci_portname(ps), write ? "write" : "read", size, pos));
1156433d6423SLionel Sambuc
1157433d6423SLionel Sambuc assert(ps->state == STATE_GOOD_DEV);
1158433d6423SLionel Sambuc assert(ps->flags & FLAG_HAS_MEDIUM);
1159433d6423SLionel Sambuc assert(ps->sector_size > 0);
1160433d6423SLionel Sambuc
1161433d6423SLionel Sambuc /* Limit the maximum size of a single transfer.
1162433d6423SLionel Sambuc * See the comments at the top of this file for details.
1163433d6423SLionel Sambuc */
1164433d6423SLionel Sambuc if (size > MAX_TRANSFER)
1165433d6423SLionel Sambuc size = MAX_TRANSFER;
1166433d6423SLionel Sambuc
1167433d6423SLionel Sambuc /* If necessary, reduce the request size so that the request does not
1168433d6423SLionel Sambuc * extend beyond the end of the partition. The caller already
1169433d6423SLionel Sambuc * guarantees that the starting position lies within the partition.
1170433d6423SLionel Sambuc */
1171433d6423SLionel Sambuc if (pos + size > eof)
1172433d6423SLionel Sambuc size = (vir_bytes) (eof - pos);
1173433d6423SLionel Sambuc
1174433d6423SLionel Sambuc start_lba = pos / ps->sector_size;
1175433d6423SLionel Sambuc lead = (vir_bytes) (pos % ps->sector_size);
1176433d6423SLionel Sambuc count = (lead + size + ps->sector_size - 1) / ps->sector_size;
1177433d6423SLionel Sambuc
1178433d6423SLionel Sambuc /* Position must be word-aligned for read requests, and sector-aligned
1179433d6423SLionel Sambuc * for write requests. We do not support read-modify-write for writes.
1180433d6423SLionel Sambuc */
1181433d6423SLionel Sambuc if ((lead & 1) || (write && lead != 0)) {
1182433d6423SLionel Sambuc dprintf(V_ERR, ("%s: unaligned position from %d\n",
1183433d6423SLionel Sambuc ahci_portname(ps), endpt));
1184433d6423SLionel Sambuc return EINVAL;
1185433d6423SLionel Sambuc }
1186433d6423SLionel Sambuc
1187433d6423SLionel Sambuc /* Write requests must be sector-aligned. Word alignment of the size is
1188433d6423SLionel Sambuc * already guaranteed by sum_iovec().
1189433d6423SLionel Sambuc */
1190433d6423SLionel Sambuc if (write && (size % ps->sector_size) != 0) {
1191433d6423SLionel Sambuc dprintf(V_ERR, ("%s: unaligned size %lu from %d\n",
1192433d6423SLionel Sambuc ahci_portname(ps), size, endpt));
1193433d6423SLionel Sambuc return EINVAL;
1194433d6423SLionel Sambuc }
1195433d6423SLionel Sambuc
1196433d6423SLionel Sambuc /* Create a vector of physical addresses and sizes for the transfer. */
1197433d6423SLionel Sambuc nr_prds = r = setup_prdt(ps, endpt, iovec, nr_req, size, lead, write,
1198433d6423SLionel Sambuc prdt);
1199433d6423SLionel Sambuc
1200433d6423SLionel Sambuc if (r < 0) return r;
1201433d6423SLionel Sambuc
1202433d6423SLionel Sambuc /* Perform the actual transfer. */
1203433d6423SLionel Sambuc cmd = port_find_cmd(ps);
1204433d6423SLionel Sambuc
1205433d6423SLionel Sambuc if (ps->flags & FLAG_ATAPI)
1206433d6423SLionel Sambuc r = atapi_transfer(ps, cmd, start_lba, count, write, prdt,
1207433d6423SLionel Sambuc nr_prds);
1208433d6423SLionel Sambuc else
1209433d6423SLionel Sambuc r = ata_transfer(ps, cmd, start_lba, count, write,
1210433d6423SLionel Sambuc !!(flags & BDEV_FORCEWRITE), prdt, nr_prds);
1211433d6423SLionel Sambuc
1212433d6423SLionel Sambuc if (r != OK) return r;
1213433d6423SLionel Sambuc
1214433d6423SLionel Sambuc return size;
1215433d6423SLionel Sambuc }
1216433d6423SLionel Sambuc
1217433d6423SLionel Sambuc /*===========================================================================*
1218433d6423SLionel Sambuc * port_hardreset *
1219433d6423SLionel Sambuc *===========================================================================*/
port_hardreset(struct port_state * ps)1220433d6423SLionel Sambuc static void port_hardreset(struct port_state *ps)
1221433d6423SLionel Sambuc {
1222433d6423SLionel Sambuc /* Perform a port-level (hard) reset on the given port.
1223433d6423SLionel Sambuc */
1224433d6423SLionel Sambuc
1225433d6423SLionel Sambuc port_write(ps, AHCI_PORT_SCTL, AHCI_PORT_SCTL_DET_INIT);
1226433d6423SLionel Sambuc
1227433d6423SLionel Sambuc micro_delay(COMRESET_DELAY * 1000); /* COMRESET_DELAY is in ms */
1228433d6423SLionel Sambuc
1229433d6423SLionel Sambuc port_write(ps, AHCI_PORT_SCTL, AHCI_PORT_SCTL_DET_NONE);
1230433d6423SLionel Sambuc }
1231433d6423SLionel Sambuc
1232433d6423SLionel Sambuc /*===========================================================================*
1233433d6423SLionel Sambuc * port_override *
1234433d6423SLionel Sambuc *===========================================================================*/
port_override(struct port_state * ps)1235433d6423SLionel Sambuc static void port_override(struct port_state *ps)
1236433d6423SLionel Sambuc {
1237433d6423SLionel Sambuc /* Override the port's BSY and/or DRQ flags. This may only be done
1238433d6423SLionel Sambuc * prior to starting the port.
1239433d6423SLionel Sambuc */
1240433d6423SLionel Sambuc u32_t cmd;
1241433d6423SLionel Sambuc
1242433d6423SLionel Sambuc cmd = port_read(ps, AHCI_PORT_CMD);
1243433d6423SLionel Sambuc port_write(ps, AHCI_PORT_CMD, cmd | AHCI_PORT_CMD_CLO);
1244433d6423SLionel Sambuc
1245433d6423SLionel Sambuc SPIN_UNTIL(!(port_read(ps, AHCI_PORT_CMD) & AHCI_PORT_CMD_CLO),
1246433d6423SLionel Sambuc PORTREG_DELAY);
1247433d6423SLionel Sambuc
1248433d6423SLionel Sambuc dprintf(V_INFO, ("%s: overridden\n", ahci_portname(ps)));
1249433d6423SLionel Sambuc }
1250433d6423SLionel Sambuc
1251433d6423SLionel Sambuc /*===========================================================================*
1252433d6423SLionel Sambuc * port_start *
1253433d6423SLionel Sambuc *===========================================================================*/
port_start(struct port_state * ps)1254433d6423SLionel Sambuc static void port_start(struct port_state *ps)
1255433d6423SLionel Sambuc {
1256433d6423SLionel Sambuc /* Start the given port, allowing for the execution of commands and the
1257433d6423SLionel Sambuc * transfer of data on that port.
1258433d6423SLionel Sambuc */
1259433d6423SLionel Sambuc u32_t cmd;
1260433d6423SLionel Sambuc
1261433d6423SLionel Sambuc /* Reset status registers. */
1262433d6423SLionel Sambuc port_write(ps, AHCI_PORT_SERR, ~0);
1263433d6423SLionel Sambuc port_write(ps, AHCI_PORT_IS, ~0);
1264433d6423SLionel Sambuc
1265433d6423SLionel Sambuc /* Start the port. */
1266433d6423SLionel Sambuc cmd = port_read(ps, AHCI_PORT_CMD);
1267433d6423SLionel Sambuc port_write(ps, AHCI_PORT_CMD, cmd | AHCI_PORT_CMD_ST);
1268433d6423SLionel Sambuc
1269433d6423SLionel Sambuc dprintf(V_INFO, ("%s: started\n", ahci_portname(ps)));
1270433d6423SLionel Sambuc }
1271433d6423SLionel Sambuc
1272433d6423SLionel Sambuc /*===========================================================================*
1273433d6423SLionel Sambuc * port_stop *
1274433d6423SLionel Sambuc *===========================================================================*/
port_stop(struct port_state * ps)1275433d6423SLionel Sambuc static void port_stop(struct port_state *ps)
1276433d6423SLionel Sambuc {
1277433d6423SLionel Sambuc /* Stop the given port, if not already stopped.
1278433d6423SLionel Sambuc */
1279433d6423SLionel Sambuc u32_t cmd;
1280433d6423SLionel Sambuc
1281433d6423SLionel Sambuc cmd = port_read(ps, AHCI_PORT_CMD);
1282433d6423SLionel Sambuc
1283433d6423SLionel Sambuc if (cmd & (AHCI_PORT_CMD_CR | AHCI_PORT_CMD_ST)) {
1284433d6423SLionel Sambuc port_write(ps, AHCI_PORT_CMD, cmd & ~AHCI_PORT_CMD_ST);
1285433d6423SLionel Sambuc
1286433d6423SLionel Sambuc SPIN_UNTIL(!(port_read(ps, AHCI_PORT_CMD) & AHCI_PORT_CMD_CR),
1287433d6423SLionel Sambuc PORTREG_DELAY);
1288433d6423SLionel Sambuc
1289433d6423SLionel Sambuc dprintf(V_INFO, ("%s: stopped\n", ahci_portname(ps)));
1290433d6423SLionel Sambuc }
1291433d6423SLionel Sambuc }
1292433d6423SLionel Sambuc
1293433d6423SLionel Sambuc /*===========================================================================*
1294433d6423SLionel Sambuc * port_restart *
1295433d6423SLionel Sambuc *===========================================================================*/
port_restart(struct port_state * ps)1296433d6423SLionel Sambuc static void port_restart(struct port_state *ps)
1297433d6423SLionel Sambuc {
1298433d6423SLionel Sambuc /* Restart a port after a fatal error has occurred.
1299433d6423SLionel Sambuc */
1300433d6423SLionel Sambuc
1301433d6423SLionel Sambuc /* Fail all outstanding commands. */
1302433d6423SLionel Sambuc port_fail_cmds(ps);
1303433d6423SLionel Sambuc
1304433d6423SLionel Sambuc /* Stop the port. */
1305433d6423SLionel Sambuc port_stop(ps);
1306433d6423SLionel Sambuc
1307433d6423SLionel Sambuc /* If the BSY and/or DRQ flags are set, reset the port. */
1308433d6423SLionel Sambuc if (port_read(ps, AHCI_PORT_TFD) &
1309433d6423SLionel Sambuc (AHCI_PORT_TFD_STS_BSY | AHCI_PORT_TFD_STS_DRQ)) {
1310433d6423SLionel Sambuc
1311433d6423SLionel Sambuc dprintf(V_ERR, ("%s: port reset\n", ahci_portname(ps)));
1312433d6423SLionel Sambuc
1313433d6423SLionel Sambuc /* To keep this driver simple, we do not transparently recover
1314433d6423SLionel Sambuc * ongoing requests. Instead, we mark the failing device as
1315433d6423SLionel Sambuc * disconnected, and reset it. If the reset succeeds, the
1316433d6423SLionel Sambuc * device (or, perhaps, eventually, another device) will come
1317433d6423SLionel Sambuc * back up. Any current and future requests to this port will
1318433d6423SLionel Sambuc * be failed until the port is fully closed and reopened.
1319433d6423SLionel Sambuc */
1320433d6423SLionel Sambuc port_disconnect(ps);
1321433d6423SLionel Sambuc
1322433d6423SLionel Sambuc /* Trigger a port reset. */
1323433d6423SLionel Sambuc port_hardreset(ps);
1324433d6423SLionel Sambuc
1325433d6423SLionel Sambuc return;
1326433d6423SLionel Sambuc }
1327433d6423SLionel Sambuc
1328433d6423SLionel Sambuc /* Start the port. */
1329433d6423SLionel Sambuc port_start(ps);
1330433d6423SLionel Sambuc }
1331433d6423SLionel Sambuc
1332433d6423SLionel Sambuc /*===========================================================================*
1333433d6423SLionel Sambuc * print_string *
1334433d6423SLionel Sambuc *===========================================================================*/
print_string(u16_t * buf,int start,int end)1335433d6423SLionel Sambuc static void print_string(u16_t *buf, int start, int end)
1336433d6423SLionel Sambuc {
1337433d6423SLionel Sambuc /* Print a string that is stored as little-endian words and padded with
1338433d6423SLionel Sambuc * trailing spaces.
1339433d6423SLionel Sambuc */
1340433d6423SLionel Sambuc int i, last = 0;
1341433d6423SLionel Sambuc
1342433d6423SLionel Sambuc while (end >= start && buf[end] == 0x2020) end--;
1343433d6423SLionel Sambuc
1344433d6423SLionel Sambuc if (end >= start && (buf[end] & 0xFF) == 0x20) end--, last++;
1345433d6423SLionel Sambuc
1346433d6423SLionel Sambuc for (i = start; i <= end; i++)
1347433d6423SLionel Sambuc printf("%c%c", buf[i] >> 8, buf[i] & 0xFF);
1348433d6423SLionel Sambuc
1349433d6423SLionel Sambuc if (last)
1350433d6423SLionel Sambuc printf("%c", buf[i] >> 8);
1351433d6423SLionel Sambuc }
1352433d6423SLionel Sambuc
1353433d6423SLionel Sambuc /*===========================================================================*
1354433d6423SLionel Sambuc * port_id_check *
1355433d6423SLionel Sambuc *===========================================================================*/
port_id_check(struct port_state * ps,int success)1356433d6423SLionel Sambuc static void port_id_check(struct port_state *ps, int success)
1357433d6423SLionel Sambuc {
1358433d6423SLionel Sambuc /* The device identification command has either completed or timed out.
1359433d6423SLionel Sambuc * Decide whether this device is usable or not, and store some of its
1360433d6423SLionel Sambuc * properties.
1361433d6423SLionel Sambuc */
1362433d6423SLionel Sambuc u16_t *buf;
1363433d6423SLionel Sambuc
1364433d6423SLionel Sambuc assert(ps->state == STATE_WAIT_ID);
1365433d6423SLionel Sambuc
1366433d6423SLionel Sambuc ps->flags &= ~FLAG_BUSY;
1367433d6423SLionel Sambuc cancel_timer(&ps->cmd_info[0].timer);
1368433d6423SLionel Sambuc
1369433d6423SLionel Sambuc if (!success) {
1370433d6423SLionel Sambuc if (!(ps->flags & FLAG_ATAPI) &&
1371433d6423SLionel Sambuc port_read(ps, AHCI_PORT_SIG) != ATA_SIG_ATA) {
1372433d6423SLionel Sambuc dprintf(V_INFO, ("%s: may not be ATA, trying ATAPI\n",
1373433d6423SLionel Sambuc ahci_portname(ps)));
1374433d6423SLionel Sambuc
1375433d6423SLionel Sambuc ps->flags |= FLAG_ATAPI;
1376433d6423SLionel Sambuc
1377433d6423SLionel Sambuc (void) gen_identify(ps, FALSE /*blocking*/);
1378433d6423SLionel Sambuc return;
1379433d6423SLionel Sambuc }
1380433d6423SLionel Sambuc
1381433d6423SLionel Sambuc dprintf(V_ERR,
1382433d6423SLionel Sambuc ("%s: unable to identify\n", ahci_portname(ps)));
1383433d6423SLionel Sambuc }
1384433d6423SLionel Sambuc
1385433d6423SLionel Sambuc /* If the identify command itself succeeded, check the results and
1386433d6423SLionel Sambuc * store some properties.
1387433d6423SLionel Sambuc */
1388433d6423SLionel Sambuc if (success) {
1389433d6423SLionel Sambuc buf = (u16_t *) ps->tmp_base;
1390433d6423SLionel Sambuc
1391433d6423SLionel Sambuc if (ps->flags & FLAG_ATAPI)
1392433d6423SLionel Sambuc success = atapi_id_check(ps, buf);
1393433d6423SLionel Sambuc else
1394433d6423SLionel Sambuc success = ata_id_check(ps, buf);
1395433d6423SLionel Sambuc }
1396433d6423SLionel Sambuc
1397433d6423SLionel Sambuc /* If the device has not been identified successfully, mark it as an
1398433d6423SLionel Sambuc * unusable device.
1399433d6423SLionel Sambuc */
1400433d6423SLionel Sambuc if (!success) {
1401433d6423SLionel Sambuc port_stop(ps);
1402433d6423SLionel Sambuc
1403433d6423SLionel Sambuc ps->state = STATE_BAD_DEV;
1404433d6423SLionel Sambuc port_write(ps, AHCI_PORT_IE, AHCI_PORT_IE_PRCE);
1405433d6423SLionel Sambuc
1406433d6423SLionel Sambuc return;
1407433d6423SLionel Sambuc }
1408433d6423SLionel Sambuc
1409433d6423SLionel Sambuc /* The device has been identified successfully, and hence usable. */
1410433d6423SLionel Sambuc ps->state = STATE_GOOD_DEV;
1411433d6423SLionel Sambuc
1412433d6423SLionel Sambuc /* Print some information about the device. */
1413433d6423SLionel Sambuc if (ahci_verbose >= V_INFO) {
1414433d6423SLionel Sambuc printf("%s: ATA%s, ", ahci_portname(ps),
1415433d6423SLionel Sambuc (ps->flags & FLAG_ATAPI) ? "PI" : "");
1416433d6423SLionel Sambuc print_string(buf, 27, 46);
1417433d6423SLionel Sambuc if (ahci_verbose >= V_DEV) {
1418433d6423SLionel Sambuc printf(" (");
1419433d6423SLionel Sambuc print_string(buf, 10, 19);
1420433d6423SLionel Sambuc printf(", ");
1421433d6423SLionel Sambuc print_string(buf, 23, 26);
1422433d6423SLionel Sambuc printf(")");
1423433d6423SLionel Sambuc }
1424433d6423SLionel Sambuc
1425433d6423SLionel Sambuc if (ps->flags & FLAG_HAS_MEDIUM)
1426433d6423SLionel Sambuc printf(", %u byte sectors, %llu MB size",
1427433d6423SLionel Sambuc ps->sector_size,
1428433d6423SLionel Sambuc ps->lba_count * ps->sector_size / (1024*1024));
1429433d6423SLionel Sambuc
1430433d6423SLionel Sambuc printf("\n");
1431433d6423SLionel Sambuc }
1432433d6423SLionel Sambuc }
1433433d6423SLionel Sambuc
1434433d6423SLionel Sambuc /*===========================================================================*
1435433d6423SLionel Sambuc * port_connect *
1436433d6423SLionel Sambuc *===========================================================================*/
port_connect(struct port_state * ps)1437433d6423SLionel Sambuc static void port_connect(struct port_state *ps)
1438433d6423SLionel Sambuc {
1439433d6423SLionel Sambuc /* A device has been found to be attached to this port. Start the port,
1440433d6423SLionel Sambuc * and do timed polling for its signature to become available.
1441433d6423SLionel Sambuc */
1442433d6423SLionel Sambuc u32_t status, sig;
1443433d6423SLionel Sambuc
1444433d6423SLionel Sambuc dprintf(V_INFO, ("%s: device connected\n", ahci_portname(ps)));
1445433d6423SLionel Sambuc
1446433d6423SLionel Sambuc port_start(ps);
1447433d6423SLionel Sambuc
1448433d6423SLionel Sambuc /* The next check covers a purely hypothetical race condition, where
1449433d6423SLionel Sambuc * the device would disappear right before we try to start it. This is
1450433d6423SLionel Sambuc * possible because we have to clear PxSERR, and with that, the DIAG.N
1451433d6423SLionel Sambuc * bit. Double-check the port status, and if it is not as we expect,
1452433d6423SLionel Sambuc * infer a disconnection.
1453433d6423SLionel Sambuc */
1454433d6423SLionel Sambuc status = port_read(ps, AHCI_PORT_SSTS) & AHCI_PORT_SSTS_DET_MASK;
1455433d6423SLionel Sambuc
1456433d6423SLionel Sambuc if (status != AHCI_PORT_SSTS_DET_PHY) {
1457433d6423SLionel Sambuc dprintf(V_ERR, ("%s: device vanished!\n", ahci_portname(ps)));
1458433d6423SLionel Sambuc
1459433d6423SLionel Sambuc port_stop(ps);
1460433d6423SLionel Sambuc
1461433d6423SLionel Sambuc ps->state = STATE_NO_DEV;
1462433d6423SLionel Sambuc ps->flags &= ~FLAG_BUSY;
1463433d6423SLionel Sambuc
1464433d6423SLionel Sambuc return;
1465433d6423SLionel Sambuc }
1466433d6423SLionel Sambuc
1467433d6423SLionel Sambuc /* Clear all state flags except the busy flag, which may be relevant if
1468433d6423SLionel Sambuc * a BDEV_OPEN call is waiting for the device to become ready; the
1469433d6423SLionel Sambuc * barrier flag, which prevents access to the device until it is
1470433d6423SLionel Sambuc * completely closed and (re)opened; and, the thread suspension flag.
1471433d6423SLionel Sambuc */
1472433d6423SLionel Sambuc ps->flags &= (FLAG_BUSY | FLAG_BARRIER | FLAG_SUSPENDED);
1473433d6423SLionel Sambuc
1474433d6423SLionel Sambuc /* Check the port's signature. We only use the signature to speed up
1475433d6423SLionel Sambuc * identification; we will try both ATA and ATAPI if the signature is
1476433d6423SLionel Sambuc * neither ATA nor ATAPI.
1477433d6423SLionel Sambuc */
1478433d6423SLionel Sambuc sig = port_read(ps, AHCI_PORT_SIG);
1479433d6423SLionel Sambuc
1480433d6423SLionel Sambuc if (sig == ATA_SIG_ATAPI)
1481433d6423SLionel Sambuc ps->flags |= FLAG_ATAPI;
1482433d6423SLionel Sambuc
1483433d6423SLionel Sambuc /* Attempt to identify the device. Do this using continuation, because
1484433d6423SLionel Sambuc * we may already be called from port_wait() here, and could end up
1485433d6423SLionel Sambuc * confusing the timer expiration procedure.
1486433d6423SLionel Sambuc */
1487433d6423SLionel Sambuc ps->state = STATE_WAIT_ID;
1488433d6423SLionel Sambuc port_write(ps, AHCI_PORT_IE, AHCI_PORT_IE_MASK);
1489433d6423SLionel Sambuc
1490433d6423SLionel Sambuc (void) gen_identify(ps, FALSE /*blocking*/);
1491433d6423SLionel Sambuc }
1492433d6423SLionel Sambuc
1493433d6423SLionel Sambuc /*===========================================================================*
1494433d6423SLionel Sambuc * port_disconnect *
1495433d6423SLionel Sambuc *===========================================================================*/
port_disconnect(struct port_state * ps)1496433d6423SLionel Sambuc static void port_disconnect(struct port_state *ps)
1497433d6423SLionel Sambuc {
1498433d6423SLionel Sambuc /* The device has detached from this port. It has already been stopped.
1499433d6423SLionel Sambuc */
1500433d6423SLionel Sambuc
1501433d6423SLionel Sambuc dprintf(V_INFO, ("%s: device disconnected\n", ahci_portname(ps)));
1502433d6423SLionel Sambuc
1503433d6423SLionel Sambuc ps->state = STATE_NO_DEV;
1504433d6423SLionel Sambuc port_write(ps, AHCI_PORT_IE, AHCI_PORT_IE_PCE);
1505433d6423SLionel Sambuc ps->flags &= ~FLAG_BUSY;
1506433d6423SLionel Sambuc
1507433d6423SLionel Sambuc /* Fail any ongoing request. The caller may already have done this. */
1508433d6423SLionel Sambuc port_fail_cmds(ps);
1509433d6423SLionel Sambuc
1510433d6423SLionel Sambuc /* Block any further access until the device is completely closed and
1511433d6423SLionel Sambuc * reopened. This prevents arbitrary I/O to a newly plugged-in device
1512433d6423SLionel Sambuc * without upper layers noticing.
1513433d6423SLionel Sambuc */
1514433d6423SLionel Sambuc ps->flags |= FLAG_BARRIER;
1515433d6423SLionel Sambuc
1516433d6423SLionel Sambuc /* Inform the blockdriver library to reduce the number of threads. */
1517433d6423SLionel Sambuc blockdriver_mt_set_workers(ps->device, 1);
1518433d6423SLionel Sambuc }
1519433d6423SLionel Sambuc
1520433d6423SLionel Sambuc /*===========================================================================*
1521433d6423SLionel Sambuc * port_dev_check *
1522433d6423SLionel Sambuc *===========================================================================*/
port_dev_check(struct port_state * ps)1523433d6423SLionel Sambuc static void port_dev_check(struct port_state *ps)
1524433d6423SLionel Sambuc {
1525433d6423SLionel Sambuc /* Perform device detection by means of polling.
1526433d6423SLionel Sambuc */
1527433d6423SLionel Sambuc u32_t status, tfd;
1528433d6423SLionel Sambuc
1529433d6423SLionel Sambuc assert(ps->state == STATE_WAIT_DEV);
1530433d6423SLionel Sambuc
1531433d6423SLionel Sambuc status = port_read(ps, AHCI_PORT_SSTS) & AHCI_PORT_SSTS_DET_MASK;
1532433d6423SLionel Sambuc
1533433d6423SLionel Sambuc dprintf(V_DEV, ("%s: polled status %u\n", ahci_portname(ps), status));
1534433d6423SLionel Sambuc
1535433d6423SLionel Sambuc switch (status) {
1536433d6423SLionel Sambuc case AHCI_PORT_SSTS_DET_PHY:
1537433d6423SLionel Sambuc tfd = port_read(ps, AHCI_PORT_TFD);
1538433d6423SLionel Sambuc
1539433d6423SLionel Sambuc /* If a Phy connection has been established, and the BSY and
1540433d6423SLionel Sambuc * DRQ flags are cleared, the device is ready.
1541433d6423SLionel Sambuc */
1542433d6423SLionel Sambuc if (!(tfd & (AHCI_PORT_TFD_STS_BSY | AHCI_PORT_TFD_STS_DRQ))) {
1543433d6423SLionel Sambuc port_connect(ps);
1544433d6423SLionel Sambuc
1545433d6423SLionel Sambuc return;
1546433d6423SLionel Sambuc }
1547433d6423SLionel Sambuc
1548433d6423SLionel Sambuc /* fall-through */
1549433d6423SLionel Sambuc case AHCI_PORT_SSTS_DET_DET:
1550433d6423SLionel Sambuc /* A device has been detected, but it is not ready yet. Try for
1551433d6423SLionel Sambuc * a while before giving up. This may take seconds.
1552433d6423SLionel Sambuc */
1553433d6423SLionel Sambuc if (ps->left > 0) {
1554433d6423SLionel Sambuc ps->left--;
1555433d6423SLionel Sambuc set_timer(&ps->cmd_info[0].timer, ahci_device_delay,
1556433d6423SLionel Sambuc port_timeout, BUILD_ARG(ps - port_state, 0));
1557433d6423SLionel Sambuc return;
1558433d6423SLionel Sambuc }
1559433d6423SLionel Sambuc }
1560433d6423SLionel Sambuc
1561433d6423SLionel Sambuc dprintf(V_INFO, ("%s: device not ready\n", ahci_portname(ps)));
1562433d6423SLionel Sambuc
1563433d6423SLionel Sambuc /* We get here on timeout, and if the HBA reports that there is no
1564433d6423SLionel Sambuc * device present at all. In all cases, we change to another state.
1565433d6423SLionel Sambuc */
1566433d6423SLionel Sambuc if (status == AHCI_PORT_SSTS_DET_PHY) {
1567433d6423SLionel Sambuc /* Some devices may not correctly clear BSY/DRQ. Upon timeout,
1568433d6423SLionel Sambuc * if we can override these flags, do so and start the
1569433d6423SLionel Sambuc * identification process anyway.
1570433d6423SLionel Sambuc */
1571433d6423SLionel Sambuc if (hba_state.has_clo) {
1572433d6423SLionel Sambuc port_override(ps);
1573433d6423SLionel Sambuc
1574433d6423SLionel Sambuc port_connect(ps);
1575433d6423SLionel Sambuc
1576433d6423SLionel Sambuc return;
1577433d6423SLionel Sambuc }
1578433d6423SLionel Sambuc
1579433d6423SLionel Sambuc /* A device is present and initialized, but not ready. */
1580433d6423SLionel Sambuc ps->state = STATE_BAD_DEV;
1581433d6423SLionel Sambuc port_write(ps, AHCI_PORT_IE, AHCI_PORT_IE_PRCE);
1582433d6423SLionel Sambuc } else {
1583433d6423SLionel Sambuc /* A device may or may not be present, but it does not appear
1584433d6423SLionel Sambuc * to be ready in any case. Ignore it until the next device
1585433d6423SLionel Sambuc * initialization event.
1586433d6423SLionel Sambuc */
1587433d6423SLionel Sambuc ps->state = STATE_NO_DEV;
1588433d6423SLionel Sambuc ps->flags &= ~FLAG_BUSY;
1589433d6423SLionel Sambuc }
1590433d6423SLionel Sambuc }
1591433d6423SLionel Sambuc
1592433d6423SLionel Sambuc /*===========================================================================*
1593433d6423SLionel Sambuc * port_intr *
1594433d6423SLionel Sambuc *===========================================================================*/
port_intr(struct port_state * ps)1595433d6423SLionel Sambuc static void port_intr(struct port_state *ps)
1596433d6423SLionel Sambuc {
1597433d6423SLionel Sambuc /* Process an interrupt on this port.
1598433d6423SLionel Sambuc */
1599433d6423SLionel Sambuc u32_t smask, emask;
1600433d6423SLionel Sambuc int success;
1601433d6423SLionel Sambuc
1602433d6423SLionel Sambuc if (ps->state == STATE_NO_PORT) {
1603433d6423SLionel Sambuc dprintf(V_ERR, ("%s: interrupt for invalid port!\n",
1604433d6423SLionel Sambuc ahci_portname(ps)));
1605433d6423SLionel Sambuc
1606433d6423SLionel Sambuc return;
1607433d6423SLionel Sambuc }
1608433d6423SLionel Sambuc
1609433d6423SLionel Sambuc smask = port_read(ps, AHCI_PORT_IS);
1610433d6423SLionel Sambuc emask = smask & port_read(ps, AHCI_PORT_IE);
1611433d6423SLionel Sambuc
1612433d6423SLionel Sambuc /* Clear the interrupt flags that we saw were set. */
1613433d6423SLionel Sambuc port_write(ps, AHCI_PORT_IS, smask);
1614433d6423SLionel Sambuc
1615433d6423SLionel Sambuc dprintf(V_REQ, ("%s: interrupt (%08x)\n", ahci_portname(ps), smask));
1616433d6423SLionel Sambuc
1617433d6423SLionel Sambuc /* Check if any commands have completed. */
1618433d6423SLionel Sambuc port_check_cmds(ps);
1619433d6423SLionel Sambuc
1620433d6423SLionel Sambuc if (emask & AHCI_PORT_IS_PCS) {
1621433d6423SLionel Sambuc /* Clear the X diagnostics bit to clear this interrupt. */
1622433d6423SLionel Sambuc port_write(ps, AHCI_PORT_SERR, AHCI_PORT_SERR_DIAG_X);
1623433d6423SLionel Sambuc
1624433d6423SLionel Sambuc dprintf(V_DEV, ("%s: device attached\n", ahci_portname(ps)));
1625433d6423SLionel Sambuc
1626433d6423SLionel Sambuc switch (ps->state) {
1627433d6423SLionel Sambuc case STATE_SPIN_UP:
1628433d6423SLionel Sambuc case STATE_NO_DEV:
1629433d6423SLionel Sambuc /* Reportedly, a device has shown up. Start polling its
1630433d6423SLionel Sambuc * status until it has become ready.
1631433d6423SLionel Sambuc */
1632433d6423SLionel Sambuc
1633433d6423SLionel Sambuc if (ps->state == STATE_SPIN_UP)
1634433d6423SLionel Sambuc cancel_timer(&ps->cmd_info[0].timer);
1635433d6423SLionel Sambuc
1636433d6423SLionel Sambuc ps->state = STATE_WAIT_DEV;
1637433d6423SLionel Sambuc ps->left = ahci_device_checks;
1638433d6423SLionel Sambuc
1639433d6423SLionel Sambuc port_dev_check(ps);
1640433d6423SLionel Sambuc
1641433d6423SLionel Sambuc break;
1642433d6423SLionel Sambuc
1643433d6423SLionel Sambuc case STATE_WAIT_DEV:
1644433d6423SLionel Sambuc /* Nothing else to do. */
1645433d6423SLionel Sambuc break;
1646433d6423SLionel Sambuc
1647433d6423SLionel Sambuc default:
1648433d6423SLionel Sambuc /* Impossible. */
1649433d6423SLionel Sambuc assert(0);
1650433d6423SLionel Sambuc }
1651433d6423SLionel Sambuc } else if (emask & AHCI_PORT_IS_PRCS) {
1652433d6423SLionel Sambuc /* Clear the N diagnostics bit to clear this interrupt. */
1653433d6423SLionel Sambuc port_write(ps, AHCI_PORT_SERR, AHCI_PORT_SERR_DIAG_N);
1654433d6423SLionel Sambuc
1655433d6423SLionel Sambuc dprintf(V_DEV, ("%s: device detached\n", ahci_portname(ps)));
1656433d6423SLionel Sambuc
1657433d6423SLionel Sambuc switch (ps->state) {
1658433d6423SLionel Sambuc case STATE_WAIT_ID:
1659433d6423SLionel Sambuc case STATE_GOOD_DEV:
1660433d6423SLionel Sambuc /* The device is no longer ready. Stop the port, cancel
1661433d6423SLionel Sambuc * ongoing requests, and disconnect the device.
1662433d6423SLionel Sambuc */
1663433d6423SLionel Sambuc port_stop(ps);
1664433d6423SLionel Sambuc
1665433d6423SLionel Sambuc /* fall-through */
1666433d6423SLionel Sambuc case STATE_BAD_DEV:
1667433d6423SLionel Sambuc port_disconnect(ps);
1668433d6423SLionel Sambuc
1669433d6423SLionel Sambuc /* The device has become unusable to us at this point.
1670433d6423SLionel Sambuc * Reset the port to make sure that once the device (or
1671433d6423SLionel Sambuc * another device) becomes usable again, we will get a
1672433d6423SLionel Sambuc * PCS interrupt as well.
1673433d6423SLionel Sambuc */
1674433d6423SLionel Sambuc port_hardreset(ps);
1675433d6423SLionel Sambuc
1676433d6423SLionel Sambuc break;
1677433d6423SLionel Sambuc
1678433d6423SLionel Sambuc default:
1679433d6423SLionel Sambuc /* Impossible. */
1680433d6423SLionel Sambuc assert(0);
1681433d6423SLionel Sambuc }
1682433d6423SLionel Sambuc } else if (smask & AHCI_PORT_IS_MASK) {
1683433d6423SLionel Sambuc /* We assume that any other interrupt indicates command
1684433d6423SLionel Sambuc * completion or (command or device) failure. Unfortunately, if
1685433d6423SLionel Sambuc * an NCQ command failed, we cannot easily determine which one
1686433d6423SLionel Sambuc * it was. For that reason, after completing all successfully
1687433d6423SLionel Sambuc * finished commands (above), we fail all other outstanding
1688433d6423SLionel Sambuc * commands and restart the port. This can possibly be improved
1689433d6423SLionel Sambuc * later by obtaining per-command status results from the HBA.
1690433d6423SLionel Sambuc */
1691433d6423SLionel Sambuc
1692433d6423SLionel Sambuc success = !(port_read(ps, AHCI_PORT_TFD) &
1693433d6423SLionel Sambuc (AHCI_PORT_TFD_STS_ERR | AHCI_PORT_TFD_STS_DF));
1694433d6423SLionel Sambuc
1695433d6423SLionel Sambuc /* Check now for failure. There are fatal failures, and there
1696433d6423SLionel Sambuc * are failures that set the TFD.STS.ERR field using a D2H
1697433d6423SLionel Sambuc * FIS. In both cases, we just restart the port, failing all
1698433d6423SLionel Sambuc * commands in the process.
1699433d6423SLionel Sambuc */
1700433d6423SLionel Sambuc if ((port_read(ps, AHCI_PORT_TFD) &
1701433d6423SLionel Sambuc (AHCI_PORT_TFD_STS_ERR | AHCI_PORT_TFD_STS_DF)) ||
1702433d6423SLionel Sambuc (smask & AHCI_PORT_IS_RESTART)) {
1703433d6423SLionel Sambuc port_restart(ps);
1704433d6423SLionel Sambuc }
1705433d6423SLionel Sambuc
1706433d6423SLionel Sambuc /* If we were waiting for ID verification, check now. */
1707433d6423SLionel Sambuc if (ps->state == STATE_WAIT_ID)
1708433d6423SLionel Sambuc port_id_check(ps, success);
1709433d6423SLionel Sambuc }
1710433d6423SLionel Sambuc }
1711433d6423SLionel Sambuc
1712433d6423SLionel Sambuc /*===========================================================================*
1713433d6423SLionel Sambuc * port_timeout *
1714433d6423SLionel Sambuc *===========================================================================*/
port_timeout(int arg)1715*cfd712b4SDavid van Moolenbroek static void port_timeout(int arg)
1716433d6423SLionel Sambuc {
1717433d6423SLionel Sambuc /* A timeout has occurred on this port. Figure out what the timeout is
1718433d6423SLionel Sambuc * for, and take appropriate action.
1719433d6423SLionel Sambuc */
1720433d6423SLionel Sambuc struct port_state *ps;
1721433d6423SLionel Sambuc int port, cmd;
1722433d6423SLionel Sambuc
1723*cfd712b4SDavid van Moolenbroek port = GET_PORT(arg);
1724*cfd712b4SDavid van Moolenbroek cmd = GET_TAG(arg);
1725433d6423SLionel Sambuc
1726433d6423SLionel Sambuc assert(port >= 0 && port < hba_state.nr_ports);
1727433d6423SLionel Sambuc
1728433d6423SLionel Sambuc ps = &port_state[port];
1729433d6423SLionel Sambuc
1730433d6423SLionel Sambuc /* Regardless of the outcome of this timeout, wake up the thread if it
1731433d6423SLionel Sambuc * is suspended. This applies only during the initialization.
1732433d6423SLionel Sambuc */
1733433d6423SLionel Sambuc if (ps->flags & FLAG_SUSPENDED) {
1734433d6423SLionel Sambuc assert(cmd == 0);
1735433d6423SLionel Sambuc blockdriver_mt_wakeup(ps->cmd_info[0].tid);
1736433d6423SLionel Sambuc }
1737433d6423SLionel Sambuc
1738433d6423SLionel Sambuc /* If detection of a device after startup timed out, give up on initial
1739433d6423SLionel Sambuc * detection and only look for hot plug events from now on.
1740433d6423SLionel Sambuc */
1741433d6423SLionel Sambuc if (ps->state == STATE_SPIN_UP) {
1742433d6423SLionel Sambuc /* One exception: if the PCS interrupt bit is set here, then we
1743433d6423SLionel Sambuc * are probably running on VirtualBox, which is currently not
1744433d6423SLionel Sambuc * always raising interrupts when setting interrupt bits (!).
1745433d6423SLionel Sambuc */
1746433d6423SLionel Sambuc if (port_read(ps, AHCI_PORT_IS) & AHCI_PORT_IS_PCS) {
1747433d6423SLionel Sambuc dprintf(V_INFO, ("%s: bad controller, no interrupt\n",
1748433d6423SLionel Sambuc ahci_portname(ps)));
1749433d6423SLionel Sambuc
1750433d6423SLionel Sambuc ps->state = STATE_WAIT_DEV;
1751433d6423SLionel Sambuc ps->left = ahci_device_checks;
1752433d6423SLionel Sambuc
1753433d6423SLionel Sambuc port_dev_check(ps);
1754433d6423SLionel Sambuc
1755433d6423SLionel Sambuc return;
1756433d6423SLionel Sambuc } else {
1757433d6423SLionel Sambuc dprintf(V_INFO, ("%s: spin-up timeout\n",
1758433d6423SLionel Sambuc ahci_portname(ps)));
1759433d6423SLionel Sambuc
1760433d6423SLionel Sambuc /* If the busy flag is set, a BDEV_OPEN request is
1761433d6423SLionel Sambuc * waiting for the detection to finish; clear the busy
1762433d6423SLionel Sambuc * flag to return an error to the caller.
1763433d6423SLionel Sambuc */
1764433d6423SLionel Sambuc ps->state = STATE_NO_DEV;
1765433d6423SLionel Sambuc ps->flags &= ~FLAG_BUSY;
1766433d6423SLionel Sambuc }
1767433d6423SLionel Sambuc
1768433d6423SLionel Sambuc return;
1769433d6423SLionel Sambuc }
1770433d6423SLionel Sambuc
1771433d6423SLionel Sambuc /* If we are waiting for a device to become connected and initialized,
1772433d6423SLionel Sambuc * check now.
1773433d6423SLionel Sambuc */
1774433d6423SLionel Sambuc if (ps->state == STATE_WAIT_DEV) {
1775433d6423SLionel Sambuc port_dev_check(ps);
1776433d6423SLionel Sambuc
1777433d6423SLionel Sambuc return;
1778433d6423SLionel Sambuc }
1779433d6423SLionel Sambuc
1780433d6423SLionel Sambuc dprintf(V_ERR, ("%s: timeout\n", ahci_portname(ps)));
1781433d6423SLionel Sambuc
1782433d6423SLionel Sambuc /* Restart the port, failing all current commands. */
1783433d6423SLionel Sambuc port_restart(ps);
1784433d6423SLionel Sambuc
1785433d6423SLionel Sambuc /* Finish up the identify operation. */
1786433d6423SLionel Sambuc if (ps->state == STATE_WAIT_ID)
1787433d6423SLionel Sambuc port_id_check(ps, FALSE);
1788433d6423SLionel Sambuc }
1789433d6423SLionel Sambuc
1790433d6423SLionel Sambuc /*===========================================================================*
1791433d6423SLionel Sambuc * port_wait *
1792433d6423SLionel Sambuc *===========================================================================*/
port_wait(struct port_state * ps)1793433d6423SLionel Sambuc static void port_wait(struct port_state *ps)
1794433d6423SLionel Sambuc {
1795433d6423SLionel Sambuc /* Suspend the current thread until the given port is no longer busy,
1796433d6423SLionel Sambuc * due to either command completion or timeout.
1797433d6423SLionel Sambuc */
1798433d6423SLionel Sambuc
1799433d6423SLionel Sambuc ps->flags |= FLAG_SUSPENDED;
1800433d6423SLionel Sambuc
1801433d6423SLionel Sambuc while (ps->flags & FLAG_BUSY)
1802433d6423SLionel Sambuc blockdriver_mt_sleep();
1803433d6423SLionel Sambuc
1804433d6423SLionel Sambuc ps->flags &= ~FLAG_SUSPENDED;
1805433d6423SLionel Sambuc }
1806433d6423SLionel Sambuc
1807433d6423SLionel Sambuc /*===========================================================================*
1808433d6423SLionel Sambuc * port_issue *
1809433d6423SLionel Sambuc *===========================================================================*/
port_issue(struct port_state * ps,int cmd,clock_t timeout)1810433d6423SLionel Sambuc static void port_issue(struct port_state *ps, int cmd, clock_t timeout)
1811433d6423SLionel Sambuc {
1812433d6423SLionel Sambuc /* Issue a command to the port, and set a timer to trigger a timeout
1813433d6423SLionel Sambuc * if the command takes too long to complete.
1814433d6423SLionel Sambuc */
1815433d6423SLionel Sambuc
1816433d6423SLionel Sambuc /* Set the corresponding NCQ command bit, if applicable. */
1817433d6423SLionel Sambuc if (ps->flags & FLAG_HAS_NCQ)
1818433d6423SLionel Sambuc port_write(ps, AHCI_PORT_SACT, 1 << cmd);
1819433d6423SLionel Sambuc
1820433d6423SLionel Sambuc /* Make sure that the compiler does not delay any previous write
1821433d6423SLionel Sambuc * operations until after the write to the command issue register.
1822433d6423SLionel Sambuc */
1823433d6423SLionel Sambuc __insn_barrier();
1824433d6423SLionel Sambuc
1825433d6423SLionel Sambuc /* Tell the controller that a new command is ready. */
1826433d6423SLionel Sambuc port_write(ps, AHCI_PORT_CI, 1 << cmd);
1827433d6423SLionel Sambuc
1828433d6423SLionel Sambuc /* Update pending commands. */
1829433d6423SLionel Sambuc ps->pend_mask |= 1 << cmd;
1830433d6423SLionel Sambuc
1831433d6423SLionel Sambuc /* Set a timer in case the command does not complete at all. */
1832433d6423SLionel Sambuc set_timer(&ps->cmd_info[cmd].timer, timeout, port_timeout,
1833433d6423SLionel Sambuc BUILD_ARG(ps - port_state, cmd));
1834433d6423SLionel Sambuc }
1835433d6423SLionel Sambuc
1836433d6423SLionel Sambuc /*===========================================================================*
1837433d6423SLionel Sambuc * port_exec *
1838433d6423SLionel Sambuc *===========================================================================*/
port_exec(struct port_state * ps,int cmd,clock_t timeout)1839433d6423SLionel Sambuc static int port_exec(struct port_state *ps, int cmd, clock_t timeout)
1840433d6423SLionel Sambuc {
1841433d6423SLionel Sambuc /* Execute a command on a port, wait for the command to complete or for
1842433d6423SLionel Sambuc * a timeout, and return whether the command succeeded or not.
1843433d6423SLionel Sambuc */
1844433d6423SLionel Sambuc
1845433d6423SLionel Sambuc port_issue(ps, cmd, timeout);
1846433d6423SLionel Sambuc
1847433d6423SLionel Sambuc /* Put the thread to sleep until a timeout or a command completion
1848433d6423SLionel Sambuc * happens. Earlier, we used to call port_wait which set the suspended
1849433d6423SLionel Sambuc * flag. We now abandon it since the flag has to work on a per-thread,
1850433d6423SLionel Sambuc * and hence per-tag basis and not on a per-port basis. Instead, we
1851433d6423SLionel Sambuc * retain that call only to defer open calls during device/driver
1852433d6423SLionel Sambuc * initialization. Instead, we call sleep here directly. Before
1853433d6423SLionel Sambuc * sleeping, we register the thread.
1854433d6423SLionel Sambuc */
1855433d6423SLionel Sambuc ps->cmd_info[cmd].tid = blockdriver_mt_get_tid();
1856433d6423SLionel Sambuc
1857433d6423SLionel Sambuc blockdriver_mt_sleep();
1858433d6423SLionel Sambuc
1859433d6423SLionel Sambuc /* Cancelling a timer that just triggered, does no harm. */
1860433d6423SLionel Sambuc cancel_timer(&ps->cmd_info[cmd].timer);
1861433d6423SLionel Sambuc
1862433d6423SLionel Sambuc assert(!(ps->flags & FLAG_BUSY));
1863433d6423SLionel Sambuc
1864433d6423SLionel Sambuc dprintf(V_REQ, ("%s: end of command -- %s\n", ahci_portname(ps),
1865433d6423SLionel Sambuc (ps->cmd_info[cmd].result == RESULT_FAILURE) ?
1866433d6423SLionel Sambuc "failure" : "success"));
1867433d6423SLionel Sambuc
1868433d6423SLionel Sambuc if (ps->cmd_info[cmd].result == RESULT_FAILURE)
1869433d6423SLionel Sambuc return EIO;
1870433d6423SLionel Sambuc
1871433d6423SLionel Sambuc return OK;
1872433d6423SLionel Sambuc }
1873433d6423SLionel Sambuc
1874433d6423SLionel Sambuc /*===========================================================================*
1875433d6423SLionel Sambuc * port_alloc *
1876433d6423SLionel Sambuc *===========================================================================*/
port_alloc(struct port_state * ps)1877433d6423SLionel Sambuc static void port_alloc(struct port_state *ps)
1878433d6423SLionel Sambuc {
1879433d6423SLionel Sambuc /* Allocate memory for the given port, and enable FIS receipt. We try
1880433d6423SLionel Sambuc * to cram everything into one 4K-page in order to limit memory usage
1881433d6423SLionel Sambuc * as much as possible. More memory may be allocated on demand later,
1882433d6423SLionel Sambuc * but allocation failure should be fatal only here. Note that we do
1883433d6423SLionel Sambuc * not allocate memory for sector padding here, because we do not know
1884433d6423SLionel Sambuc * the device's sector size yet.
1885433d6423SLionel Sambuc */
1886433d6423SLionel Sambuc size_t fis_off, tmp_off, ct_off; int i;
1887433d6423SLionel Sambuc size_t ct_offs[NR_CMDS];
1888433d6423SLionel Sambuc u32_t cmd;
1889433d6423SLionel Sambuc
1890433d6423SLionel Sambuc fis_off = AHCI_CL_SIZE + AHCI_FIS_SIZE - 1;
1891433d6423SLionel Sambuc fis_off -= fis_off % AHCI_FIS_SIZE;
1892433d6423SLionel Sambuc
1893433d6423SLionel Sambuc tmp_off = fis_off + AHCI_FIS_SIZE + AHCI_TMP_ALIGN - 1;
1894433d6423SLionel Sambuc tmp_off -= tmp_off % AHCI_TMP_ALIGN;
1895433d6423SLionel Sambuc
1896433d6423SLionel Sambuc /* Allocate memory for all the commands. */
1897433d6423SLionel Sambuc ct_off = tmp_off + AHCI_TMP_SIZE;
1898433d6423SLionel Sambuc for (i = 0; i < NR_CMDS; i++) {
1899433d6423SLionel Sambuc ct_off += AHCI_CT_ALIGN - 1;
1900433d6423SLionel Sambuc ct_off -= ct_off % AHCI_CT_ALIGN;
1901433d6423SLionel Sambuc ct_offs[i] = ct_off;
1902433d6423SLionel Sambuc ps->mem_size = ct_off + AHCI_CT_SIZE;
1903433d6423SLionel Sambuc ct_off = ps->mem_size;
1904433d6423SLionel Sambuc }
1905433d6423SLionel Sambuc
1906433d6423SLionel Sambuc ps->mem_base = alloc_contig(ps->mem_size, AC_ALIGN4K, &ps->mem_phys);
1907433d6423SLionel Sambuc if (ps->mem_base == NULL)
1908433d6423SLionel Sambuc panic("unable to allocate port memory");
1909433d6423SLionel Sambuc memset(ps->mem_base, 0, ps->mem_size);
1910433d6423SLionel Sambuc
1911433d6423SLionel Sambuc ps->cl_base = (u32_t *) ps->mem_base;
1912433d6423SLionel Sambuc ps->cl_phys = ps->mem_phys;
1913433d6423SLionel Sambuc assert(ps->cl_phys % AHCI_CL_SIZE == 0);
1914433d6423SLionel Sambuc
1915433d6423SLionel Sambuc ps->fis_base = (u32_t *) (ps->mem_base + fis_off);
1916433d6423SLionel Sambuc ps->fis_phys = ps->mem_phys + fis_off;
1917433d6423SLionel Sambuc assert(ps->fis_phys % AHCI_FIS_SIZE == 0);
1918433d6423SLionel Sambuc
1919433d6423SLionel Sambuc ps->tmp_base = (u8_t *) (ps->mem_base + tmp_off);
1920433d6423SLionel Sambuc ps->tmp_phys = ps->mem_phys + tmp_off;
1921433d6423SLionel Sambuc assert(ps->tmp_phys % AHCI_TMP_ALIGN == 0);
1922433d6423SLionel Sambuc
1923433d6423SLionel Sambuc for (i = 0; i < NR_CMDS; i++) {
1924433d6423SLionel Sambuc ps->ct_base[i] = ps->mem_base + ct_offs[i];
1925433d6423SLionel Sambuc ps->ct_phys[i] = ps->mem_phys + ct_offs[i];
1926433d6423SLionel Sambuc assert(ps->ct_phys[i] % AHCI_CT_ALIGN == 0);
1927433d6423SLionel Sambuc }
1928433d6423SLionel Sambuc
1929433d6423SLionel Sambuc /* Tell the controller about some of the physical addresses. */
1930433d6423SLionel Sambuc port_write(ps, AHCI_PORT_FBU, 0);
1931433d6423SLionel Sambuc port_write(ps, AHCI_PORT_FB, ps->fis_phys);
1932433d6423SLionel Sambuc
1933433d6423SLionel Sambuc port_write(ps, AHCI_PORT_CLBU, 0);
1934433d6423SLionel Sambuc port_write(ps, AHCI_PORT_CLB, ps->cl_phys);
1935433d6423SLionel Sambuc
1936433d6423SLionel Sambuc /* Enable FIS receive. */
1937433d6423SLionel Sambuc cmd = port_read(ps, AHCI_PORT_CMD);
1938433d6423SLionel Sambuc port_write(ps, AHCI_PORT_CMD, cmd | AHCI_PORT_CMD_FRE);
1939433d6423SLionel Sambuc
1940433d6423SLionel Sambuc ps->pad_base = NULL;
1941433d6423SLionel Sambuc ps->pad_size = 0;
1942433d6423SLionel Sambuc }
1943433d6423SLionel Sambuc
1944433d6423SLionel Sambuc /*===========================================================================*
1945433d6423SLionel Sambuc * port_free *
1946433d6423SLionel Sambuc *===========================================================================*/
port_free(struct port_state * ps)1947433d6423SLionel Sambuc static void port_free(struct port_state *ps)
1948433d6423SLionel Sambuc {
1949433d6423SLionel Sambuc /* Disable FIS receipt for the given port, and free previously
1950433d6423SLionel Sambuc * allocated memory.
1951433d6423SLionel Sambuc */
1952433d6423SLionel Sambuc u32_t cmd;
1953433d6423SLionel Sambuc
1954433d6423SLionel Sambuc /* Disable FIS receive. */
1955433d6423SLionel Sambuc cmd = port_read(ps, AHCI_PORT_CMD);
1956433d6423SLionel Sambuc
1957433d6423SLionel Sambuc if (cmd & (AHCI_PORT_CMD_FR | AHCI_PORT_CMD_FRE)) {
1958433d6423SLionel Sambuc port_write(ps, AHCI_PORT_CMD, cmd & ~AHCI_PORT_CMD_FRE);
1959433d6423SLionel Sambuc
1960433d6423SLionel Sambuc SPIN_UNTIL(!(port_read(ps, AHCI_PORT_CMD) & AHCI_PORT_CMD_FR),
1961433d6423SLionel Sambuc PORTREG_DELAY);
1962433d6423SLionel Sambuc }
1963433d6423SLionel Sambuc
1964433d6423SLionel Sambuc if (ps->pad_base != NULL)
1965433d6423SLionel Sambuc free_contig(ps->pad_base, ps->pad_size);
1966433d6423SLionel Sambuc
1967433d6423SLionel Sambuc free_contig(ps->mem_base, ps->mem_size);
1968433d6423SLionel Sambuc }
1969433d6423SLionel Sambuc
1970433d6423SLionel Sambuc /*===========================================================================*
1971433d6423SLionel Sambuc * port_init *
1972433d6423SLionel Sambuc *===========================================================================*/
port_init(struct port_state * ps)1973433d6423SLionel Sambuc static void port_init(struct port_state *ps)
1974433d6423SLionel Sambuc {
1975433d6423SLionel Sambuc /* Initialize the given port.
1976433d6423SLionel Sambuc */
1977433d6423SLionel Sambuc u32_t cmd;
1978433d6423SLionel Sambuc int i;
1979433d6423SLionel Sambuc
1980433d6423SLionel Sambuc /* Initialize the port state structure. */
1981433d6423SLionel Sambuc ps->queue_depth = 1;
1982433d6423SLionel Sambuc ps->state = STATE_SPIN_UP;
1983433d6423SLionel Sambuc ps->flags = FLAG_BUSY;
1984433d6423SLionel Sambuc ps->sector_size = 0;
1985433d6423SLionel Sambuc ps->open_count = 0;
1986433d6423SLionel Sambuc ps->pend_mask = 0;
1987433d6423SLionel Sambuc for (i = 0; i < NR_CMDS; i++)
1988433d6423SLionel Sambuc init_timer(&ps->cmd_info[i].timer);
1989433d6423SLionel Sambuc
1990433d6423SLionel Sambuc ps->reg = (u32_t *) ((u8_t *) hba_state.base +
1991433d6423SLionel Sambuc AHCI_MEM_BASE_SIZE + AHCI_MEM_PORT_SIZE * (ps - port_state));
1992433d6423SLionel Sambuc
1993433d6423SLionel Sambuc /* Allocate memory for the port. */
1994433d6423SLionel Sambuc port_alloc(ps);
1995433d6423SLionel Sambuc
1996433d6423SLionel Sambuc /* Just listen for device connection events for now. */
1997433d6423SLionel Sambuc port_write(ps, AHCI_PORT_IE, AHCI_PORT_IE_PCE);
1998433d6423SLionel Sambuc
1999433d6423SLionel Sambuc /* Enable device spin-up for HBAs that support staggered spin-up.
2000433d6423SLionel Sambuc * This is a no-op for HBAs that do not support it.
2001433d6423SLionel Sambuc */
2002433d6423SLionel Sambuc cmd = port_read(ps, AHCI_PORT_CMD);
2003433d6423SLionel Sambuc port_write(ps, AHCI_PORT_CMD, cmd | AHCI_PORT_CMD_SUD);
2004433d6423SLionel Sambuc
2005433d6423SLionel Sambuc /* Trigger a port reset. */
2006433d6423SLionel Sambuc port_hardreset(ps);
2007433d6423SLionel Sambuc
2008433d6423SLionel Sambuc set_timer(&ps->cmd_info[0].timer, ahci_spinup_timeout,
2009433d6423SLionel Sambuc port_timeout, BUILD_ARG(ps - port_state, 0));
2010433d6423SLionel Sambuc }
2011433d6423SLionel Sambuc
2012433d6423SLionel Sambuc /*===========================================================================*
2013433d6423SLionel Sambuc * ahci_probe *
2014433d6423SLionel Sambuc *===========================================================================*/
ahci_probe(int skip)2015433d6423SLionel Sambuc static int ahci_probe(int skip)
2016433d6423SLionel Sambuc {
2017433d6423SLionel Sambuc /* Find a matching PCI device.
2018433d6423SLionel Sambuc */
2019433d6423SLionel Sambuc int r, devind;
2020433d6423SLionel Sambuc u16_t vid, did;
2021433d6423SLionel Sambuc
2022433d6423SLionel Sambuc pci_init();
2023433d6423SLionel Sambuc
2024433d6423SLionel Sambuc r = pci_first_dev(&devind, &vid, &did);
2025433d6423SLionel Sambuc if (r <= 0)
2026433d6423SLionel Sambuc return -1;
2027433d6423SLionel Sambuc
2028433d6423SLionel Sambuc while (skip--) {
2029433d6423SLionel Sambuc r = pci_next_dev(&devind, &vid, &did);
2030433d6423SLionel Sambuc if (r <= 0)
2031433d6423SLionel Sambuc return -1;
2032433d6423SLionel Sambuc }
2033433d6423SLionel Sambuc
2034433d6423SLionel Sambuc pci_reserve(devind);
2035433d6423SLionel Sambuc
2036433d6423SLionel Sambuc return devind;
2037433d6423SLionel Sambuc }
2038433d6423SLionel Sambuc
2039433d6423SLionel Sambuc /*===========================================================================*
2040433d6423SLionel Sambuc * ahci_reset *
2041433d6423SLionel Sambuc *===========================================================================*/
ahci_reset(void)2042433d6423SLionel Sambuc static void ahci_reset(void)
2043433d6423SLionel Sambuc {
2044433d6423SLionel Sambuc /* Reset the HBA. Do not enable AHCI mode afterwards.
2045433d6423SLionel Sambuc */
2046433d6423SLionel Sambuc u32_t ghc;
2047433d6423SLionel Sambuc
2048433d6423SLionel Sambuc ghc = hba_read(AHCI_HBA_GHC);
2049433d6423SLionel Sambuc
2050433d6423SLionel Sambuc hba_write(AHCI_HBA_GHC, ghc | AHCI_HBA_GHC_AE);
2051433d6423SLionel Sambuc
2052433d6423SLionel Sambuc hba_write(AHCI_HBA_GHC, ghc | AHCI_HBA_GHC_AE | AHCI_HBA_GHC_HR);
2053433d6423SLionel Sambuc
2054433d6423SLionel Sambuc SPIN_UNTIL(!(hba_read(AHCI_HBA_GHC) & AHCI_HBA_GHC_HR), RESET_DELAY);
2055433d6423SLionel Sambuc
2056433d6423SLionel Sambuc if (hba_read(AHCI_HBA_GHC) & AHCI_HBA_GHC_HR)
2057433d6423SLionel Sambuc panic("unable to reset HBA");
2058433d6423SLionel Sambuc }
2059433d6423SLionel Sambuc
2060433d6423SLionel Sambuc /*===========================================================================*
2061433d6423SLionel Sambuc * ahci_init *
2062433d6423SLionel Sambuc *===========================================================================*/
ahci_init(int devind)2063433d6423SLionel Sambuc static void ahci_init(int devind)
2064433d6423SLionel Sambuc {
2065433d6423SLionel Sambuc /* Initialize the device.
2066433d6423SLionel Sambuc */
2067433d6423SLionel Sambuc u32_t base, size, cap, ghc, mask;
2068433d6423SLionel Sambuc int r, port, ioflag;
2069433d6423SLionel Sambuc
2070433d6423SLionel Sambuc if ((r = pci_get_bar(devind, PCI_BAR_6, &base, &size, &ioflag)) != OK)
2071433d6423SLionel Sambuc panic("unable to retrieve BAR: %d", r);
2072433d6423SLionel Sambuc
2073433d6423SLionel Sambuc if (ioflag)
2074433d6423SLionel Sambuc panic("invalid BAR type");
2075433d6423SLionel Sambuc
2076433d6423SLionel Sambuc /* There must be at least one port, and at most NR_PORTS ports. Limit
2077433d6423SLionel Sambuc * the actual total number of ports to the size of the exposed area.
2078433d6423SLionel Sambuc */
2079433d6423SLionel Sambuc if (size < AHCI_MEM_BASE_SIZE + AHCI_MEM_PORT_SIZE)
2080433d6423SLionel Sambuc panic("HBA memory size too small: %u", size);
2081433d6423SLionel Sambuc
2082433d6423SLionel Sambuc size = MIN(size, AHCI_MEM_BASE_SIZE + AHCI_MEM_PORT_SIZE * NR_PORTS);
2083433d6423SLionel Sambuc
2084433d6423SLionel Sambuc hba_state.nr_ports = (size - AHCI_MEM_BASE_SIZE) / AHCI_MEM_PORT_SIZE;
2085433d6423SLionel Sambuc
2086433d6423SLionel Sambuc /* Map the register area into local memory. */
2087433d6423SLionel Sambuc hba_state.base = (u32_t *) vm_map_phys(SELF, (void *) base, size);
2088433d6423SLionel Sambuc hba_state.size = size;
2089433d6423SLionel Sambuc if (hba_state.base == MAP_FAILED)
2090433d6423SLionel Sambuc panic("unable to map HBA memory");
2091433d6423SLionel Sambuc
2092433d6423SLionel Sambuc /* Retrieve, allocate and enable the controller's IRQ. */
2093433d6423SLionel Sambuc hba_state.irq = pci_attr_r8(devind, PCI_ILR);
2094433d6423SLionel Sambuc hba_state.hook_id = 0;
2095433d6423SLionel Sambuc
2096433d6423SLionel Sambuc if ((r = sys_irqsetpolicy(hba_state.irq, 0, &hba_state.hook_id)) != OK)
2097433d6423SLionel Sambuc panic("unable to register IRQ: %d", r);
2098433d6423SLionel Sambuc
2099433d6423SLionel Sambuc if ((r = sys_irqenable(&hba_state.hook_id)) != OK)
2100433d6423SLionel Sambuc panic("unable to enable IRQ: %d", r);
2101433d6423SLionel Sambuc
2102433d6423SLionel Sambuc /* Reset the HBA. */
2103433d6423SLionel Sambuc ahci_reset();
2104433d6423SLionel Sambuc
2105433d6423SLionel Sambuc /* Enable AHCI and interrupts. */
2106433d6423SLionel Sambuc ghc = hba_read(AHCI_HBA_GHC);
2107433d6423SLionel Sambuc hba_write(AHCI_HBA_GHC, ghc | AHCI_HBA_GHC_AE | AHCI_HBA_GHC_IE);
2108433d6423SLionel Sambuc
2109433d6423SLionel Sambuc /* Limit the maximum number of commands to the controller's value. */
2110433d6423SLionel Sambuc /* Note that we currently use only one command anyway. */
2111433d6423SLionel Sambuc cap = hba_read(AHCI_HBA_CAP);
2112433d6423SLionel Sambuc hba_state.has_ncq = !!(cap & AHCI_HBA_CAP_SNCQ);
2113433d6423SLionel Sambuc hba_state.has_clo = !!(cap & AHCI_HBA_CAP_SCLO);
2114433d6423SLionel Sambuc hba_state.nr_cmds = MIN(NR_CMDS,
2115433d6423SLionel Sambuc ((cap >> AHCI_HBA_CAP_NCS_SHIFT) & AHCI_HBA_CAP_NCS_MASK) + 1);
2116433d6423SLionel Sambuc
2117433d6423SLionel Sambuc dprintf(V_INFO, ("AHCI%u: HBA v%d.%d%d, %ld ports, %ld commands, "
2118433d6423SLionel Sambuc "%s queuing, IRQ %d\n",
2119433d6423SLionel Sambuc ahci_instance,
2120433d6423SLionel Sambuc (int) (hba_read(AHCI_HBA_VS) >> 16),
2121433d6423SLionel Sambuc (int) ((hba_read(AHCI_HBA_VS) >> 8) & 0xFF),
2122433d6423SLionel Sambuc (int) (hba_read(AHCI_HBA_VS) & 0xFF),
2123433d6423SLionel Sambuc ((cap >> AHCI_HBA_CAP_NP_SHIFT) & AHCI_HBA_CAP_NP_MASK) + 1,
2124433d6423SLionel Sambuc ((cap >> AHCI_HBA_CAP_NCS_SHIFT) & AHCI_HBA_CAP_NCS_MASK) + 1,
2125433d6423SLionel Sambuc hba_state.has_ncq ? "supports" : "no", hba_state.irq));
2126433d6423SLionel Sambuc
2127433d6423SLionel Sambuc dprintf(V_INFO, ("AHCI%u: CAP %08x, CAP2 %08x, PI %08x\n",
2128433d6423SLionel Sambuc ahci_instance, cap, hba_read(AHCI_HBA_CAP2),
2129433d6423SLionel Sambuc hba_read(AHCI_HBA_PI)));
2130433d6423SLionel Sambuc
2131433d6423SLionel Sambuc /* Initialize each of the implemented ports. We ignore CAP.NP. */
2132433d6423SLionel Sambuc mask = hba_read(AHCI_HBA_PI);
2133433d6423SLionel Sambuc
2134433d6423SLionel Sambuc for (port = 0; port < hba_state.nr_ports; port++) {
2135433d6423SLionel Sambuc port_state[port].device = NO_DEVICE;
2136433d6423SLionel Sambuc port_state[port].state = STATE_NO_PORT;
2137433d6423SLionel Sambuc
2138433d6423SLionel Sambuc if (mask & (1 << port))
2139433d6423SLionel Sambuc port_init(&port_state[port]);
2140433d6423SLionel Sambuc }
2141433d6423SLionel Sambuc }
2142433d6423SLionel Sambuc
2143433d6423SLionel Sambuc /*===========================================================================*
2144433d6423SLionel Sambuc * ahci_stop *
2145433d6423SLionel Sambuc *===========================================================================*/
ahci_stop(void)2146433d6423SLionel Sambuc static void ahci_stop(void)
2147433d6423SLionel Sambuc {
2148433d6423SLionel Sambuc /* Disable AHCI, and clean up resources to the extent possible.
2149433d6423SLionel Sambuc */
2150433d6423SLionel Sambuc struct port_state *ps;
2151433d6423SLionel Sambuc int r, port;
2152433d6423SLionel Sambuc
2153433d6423SLionel Sambuc for (port = 0; port < hba_state.nr_ports; port++) {
2154433d6423SLionel Sambuc ps = &port_state[port];
2155433d6423SLionel Sambuc
2156433d6423SLionel Sambuc if (ps->state != STATE_NO_PORT) {
2157433d6423SLionel Sambuc port_stop(ps);
2158433d6423SLionel Sambuc
2159433d6423SLionel Sambuc port_free(ps);
2160433d6423SLionel Sambuc }
2161433d6423SLionel Sambuc }
2162433d6423SLionel Sambuc
2163433d6423SLionel Sambuc ahci_reset();
2164433d6423SLionel Sambuc
2165433d6423SLionel Sambuc if ((r = vm_unmap_phys(SELF, (void *) hba_state.base,
2166433d6423SLionel Sambuc hba_state.size)) != OK)
2167433d6423SLionel Sambuc panic("unable to unmap HBA memory: %d", r);
2168433d6423SLionel Sambuc
2169433d6423SLionel Sambuc if ((r = sys_irqrmpolicy(&hba_state.hook_id)) != OK)
2170433d6423SLionel Sambuc panic("unable to deregister IRQ: %d", r);
2171433d6423SLionel Sambuc }
2172433d6423SLionel Sambuc
2173433d6423SLionel Sambuc /*===========================================================================*
2174433d6423SLionel Sambuc * ahci_alarm *
2175433d6423SLionel Sambuc *===========================================================================*/
ahci_alarm(clock_t stamp)2176433d6423SLionel Sambuc static void ahci_alarm(clock_t stamp)
2177433d6423SLionel Sambuc {
2178433d6423SLionel Sambuc /* Process an alarm.
2179433d6423SLionel Sambuc */
2180433d6423SLionel Sambuc
2181433d6423SLionel Sambuc /* Call the port-specific handler for each port that timed out. */
2182433d6423SLionel Sambuc expire_timers(stamp);
2183433d6423SLionel Sambuc }
2184433d6423SLionel Sambuc
2185433d6423SLionel Sambuc /*===========================================================================*
2186433d6423SLionel Sambuc * ahci_intr *
2187433d6423SLionel Sambuc *===========================================================================*/
ahci_intr(unsigned int UNUSED (mask))2188433d6423SLionel Sambuc static void ahci_intr(unsigned int UNUSED(mask))
2189433d6423SLionel Sambuc {
2190433d6423SLionel Sambuc /* Process an interrupt.
2191433d6423SLionel Sambuc */
2192433d6423SLionel Sambuc struct port_state *ps;
2193433d6423SLionel Sambuc u32_t mask;
2194433d6423SLionel Sambuc int r, port;
2195433d6423SLionel Sambuc
2196433d6423SLionel Sambuc /* Handle an interrupt for each port that has the interrupt bit set. */
2197433d6423SLionel Sambuc mask = hba_read(AHCI_HBA_IS);
2198433d6423SLionel Sambuc
2199433d6423SLionel Sambuc for (port = 0; port < hba_state.nr_ports; port++) {
2200433d6423SLionel Sambuc if (mask & (1 << port)) {
2201433d6423SLionel Sambuc ps = &port_state[port];
2202433d6423SLionel Sambuc
2203433d6423SLionel Sambuc port_intr(ps);
2204433d6423SLionel Sambuc
2205433d6423SLionel Sambuc /* After processing an interrupt, wake up the device
2206433d6423SLionel Sambuc * thread if it is suspended and now no longer busy.
2207433d6423SLionel Sambuc */
2208433d6423SLionel Sambuc if ((ps->flags & (FLAG_SUSPENDED | FLAG_BUSY)) ==
2209433d6423SLionel Sambuc FLAG_SUSPENDED)
2210433d6423SLionel Sambuc blockdriver_mt_wakeup(ps->cmd_info[0].tid);
2211433d6423SLionel Sambuc }
2212433d6423SLionel Sambuc }
2213433d6423SLionel Sambuc
2214433d6423SLionel Sambuc /* Clear the bits that we processed. */
2215433d6423SLionel Sambuc hba_write(AHCI_HBA_IS, mask);
2216433d6423SLionel Sambuc
2217433d6423SLionel Sambuc /* Reenable the interrupt. */
2218433d6423SLionel Sambuc if ((r = sys_irqenable(&hba_state.hook_id)) != OK)
2219433d6423SLionel Sambuc panic("unable to enable IRQ: %d", r);
2220433d6423SLionel Sambuc }
2221433d6423SLionel Sambuc
2222433d6423SLionel Sambuc /*===========================================================================*
2223433d6423SLionel Sambuc * ahci_get_params *
2224433d6423SLionel Sambuc *===========================================================================*/
ahci_get_params(void)2225433d6423SLionel Sambuc static void ahci_get_params(void)
2226433d6423SLionel Sambuc {
2227433d6423SLionel Sambuc /* Retrieve and parse parameters passed to this driver, except the
2228433d6423SLionel Sambuc * device-to-port mapping, which has to be parsed later.
2229433d6423SLionel Sambuc */
2230433d6423SLionel Sambuc long v;
2231433d6423SLionel Sambuc unsigned int i;
2232433d6423SLionel Sambuc
2233433d6423SLionel Sambuc /* Find out which driver instance we are. */
2234433d6423SLionel Sambuc v = 0;
2235433d6423SLionel Sambuc (void) env_parse("instance", "d", 0, &v, 0, 255);
2236433d6423SLionel Sambuc ahci_instance = (int) v;
2237433d6423SLionel Sambuc
2238433d6423SLionel Sambuc /* Initialize the verbosity level. */
2239433d6423SLionel Sambuc v = V_ERR;
2240433d6423SLionel Sambuc (void) env_parse("ahci_verbose", "d", 0, &v, V_NONE, V_REQ);
2241433d6423SLionel Sambuc ahci_verbose = (int) v;
2242433d6423SLionel Sambuc
2243433d6423SLionel Sambuc /* Initialize timeout-related values. */
2244433d6423SLionel Sambuc for (i = 0; i < sizeof(ahci_timevar) / sizeof(ahci_timevar[0]); i++) {
2245433d6423SLionel Sambuc v = ahci_timevar[i].default_ms;
2246433d6423SLionel Sambuc
2247433d6423SLionel Sambuc (void) env_parse(ahci_timevar[i].name, "d", 0, &v, 1,
2248433d6423SLionel Sambuc LONG_MAX);
2249433d6423SLionel Sambuc
2250433d6423SLionel Sambuc *ahci_timevar[i].ptr = millis_to_hz(v);
2251433d6423SLionel Sambuc }
2252433d6423SLionel Sambuc
2253433d6423SLionel Sambuc ahci_device_delay = millis_to_hz(DEVICE_DELAY);
2254433d6423SLionel Sambuc ahci_device_checks = (ahci_device_timeout + ahci_device_delay - 1) /
2255433d6423SLionel Sambuc ahci_device_delay;
2256433d6423SLionel Sambuc }
2257433d6423SLionel Sambuc
2258433d6423SLionel Sambuc /*===========================================================================*
2259433d6423SLionel Sambuc * ahci_set_mapping *
2260433d6423SLionel Sambuc *===========================================================================*/
ahci_set_mapping(void)2261433d6423SLionel Sambuc static void ahci_set_mapping(void)
2262433d6423SLionel Sambuc {
2263433d6423SLionel Sambuc /* Construct a mapping from device nodes to port numbers.
2264433d6423SLionel Sambuc */
2265433d6423SLionel Sambuc char key[16], val[32], *p;
2266433d6423SLionel Sambuc unsigned int port;
2267433d6423SLionel Sambuc int i, j;
2268433d6423SLionel Sambuc
2269433d6423SLionel Sambuc /* Start off with a mapping that includes implemented ports only, in
2270433d6423SLionel Sambuc * order. We choose this mapping over an identity mapping to maximize
2271433d6423SLionel Sambuc * the chance that the user will be able to access the first MAX_DRIVES
2272433d6423SLionel Sambuc * devices. Note that we can only do this after initializing the HBA.
2273433d6423SLionel Sambuc */
2274433d6423SLionel Sambuc for (i = j = 0; i < NR_PORTS && j < MAX_DRIVES; i++)
2275433d6423SLionel Sambuc if (port_state[i].state != STATE_NO_PORT)
2276433d6423SLionel Sambuc ahci_map[j++] = i;
2277433d6423SLionel Sambuc
2278433d6423SLionel Sambuc for ( ; j < MAX_DRIVES; j++)
2279433d6423SLionel Sambuc ahci_map[j] = NO_PORT;
2280433d6423SLionel Sambuc
2281433d6423SLionel Sambuc /* See if the user specified a custom mapping. Unlike all other
2282433d6423SLionel Sambuc * configuration options, this is a per-instance setting.
2283433d6423SLionel Sambuc */
2284433d6423SLionel Sambuc strlcpy(key, "ahci0_map", sizeof(key));
2285433d6423SLionel Sambuc key[4] += ahci_instance;
2286433d6423SLionel Sambuc
2287433d6423SLionel Sambuc if (env_get_param(key, val, sizeof(val)) == OK) {
2288433d6423SLionel Sambuc /* Parse the mapping, which is assumed to be a comma-separated
2289433d6423SLionel Sambuc * list of zero-based port numbers.
2290433d6423SLionel Sambuc */
2291433d6423SLionel Sambuc p = val;
2292433d6423SLionel Sambuc
2293433d6423SLionel Sambuc for (i = 0; i < MAX_DRIVES; i++) {
2294433d6423SLionel Sambuc if (*p) {
2295433d6423SLionel Sambuc port = (unsigned int) strtoul(p, &p, 0);
2296433d6423SLionel Sambuc
2297433d6423SLionel Sambuc if (*p) p++;
2298433d6423SLionel Sambuc
2299433d6423SLionel Sambuc ahci_map[i] = port % NR_PORTS;
2300433d6423SLionel Sambuc }
2301433d6423SLionel Sambuc else ahci_map[i] = NO_PORT;
2302433d6423SLionel Sambuc }
2303433d6423SLionel Sambuc }
2304433d6423SLionel Sambuc
2305433d6423SLionel Sambuc /* Create a reverse mapping. */
2306433d6423SLionel Sambuc for (i = 0; i < MAX_DRIVES; i++)
2307433d6423SLionel Sambuc if ((j = ahci_map[i]) != NO_PORT)
2308433d6423SLionel Sambuc port_state[j].device = i;
2309433d6423SLionel Sambuc }
2310433d6423SLionel Sambuc
2311433d6423SLionel Sambuc /*===========================================================================*
2312433d6423SLionel Sambuc * sef_cb_init_fresh *
2313433d6423SLionel Sambuc *===========================================================================*/
sef_cb_init_fresh(int type,sef_init_info_t * UNUSED (info))2314433d6423SLionel Sambuc static int sef_cb_init_fresh(int type, sef_init_info_t *UNUSED(info))
2315433d6423SLionel Sambuc {
2316433d6423SLionel Sambuc /* Initialize the driver.
2317433d6423SLionel Sambuc */
2318433d6423SLionel Sambuc int devind;
2319433d6423SLionel Sambuc
2320433d6423SLionel Sambuc /* Get command line parameters. */
2321433d6423SLionel Sambuc ahci_get_params();
2322433d6423SLionel Sambuc
2323433d6423SLionel Sambuc /* Probe for recognized devices, skipping matches as appropriate. */
2324433d6423SLionel Sambuc devind = ahci_probe(ahci_instance);
2325433d6423SLionel Sambuc
2326433d6423SLionel Sambuc if (devind < 0)
2327433d6423SLionel Sambuc panic("no matching device found");
2328433d6423SLionel Sambuc
2329433d6423SLionel Sambuc /* Initialize the device we found. */
2330433d6423SLionel Sambuc ahci_init(devind);
2331433d6423SLionel Sambuc
2332433d6423SLionel Sambuc /* Create a mapping from device nodes to port numbers. */
2333433d6423SLionel Sambuc ahci_set_mapping();
2334433d6423SLionel Sambuc
2335433d6423SLionel Sambuc /* Announce that we are up. */
2336433d6423SLionel Sambuc blockdriver_announce(type);
2337433d6423SLionel Sambuc
2338433d6423SLionel Sambuc return OK;
2339433d6423SLionel Sambuc }
2340433d6423SLionel Sambuc
2341433d6423SLionel Sambuc /*===========================================================================*
2342433d6423SLionel Sambuc * sef_cb_signal_handler *
2343433d6423SLionel Sambuc *===========================================================================*/
sef_cb_signal_handler(int signo)2344433d6423SLionel Sambuc static void sef_cb_signal_handler(int signo)
2345433d6423SLionel Sambuc {
2346433d6423SLionel Sambuc /* In case of a termination signal, shut down this driver.
2347433d6423SLionel Sambuc */
2348433d6423SLionel Sambuc int port;
2349433d6423SLionel Sambuc
2350433d6423SLionel Sambuc if (signo != SIGTERM) return;
2351433d6423SLionel Sambuc
2352433d6423SLionel Sambuc /* If any ports are still opened, assume that the system is being shut
2353433d6423SLionel Sambuc * down, and stay up until the last device has been closed.
2354433d6423SLionel Sambuc */
2355433d6423SLionel Sambuc ahci_exiting = TRUE;
2356433d6423SLionel Sambuc
2357433d6423SLionel Sambuc for (port = 0; port < hba_state.nr_ports; port++)
2358433d6423SLionel Sambuc if (port_state[port].open_count > 0)
2359433d6423SLionel Sambuc return;
2360433d6423SLionel Sambuc
2361433d6423SLionel Sambuc /* If not, stop the driver and exit immediately. */
2362433d6423SLionel Sambuc ahci_stop();
2363433d6423SLionel Sambuc
2364433d6423SLionel Sambuc exit(0);
2365433d6423SLionel Sambuc }
2366433d6423SLionel Sambuc
2367433d6423SLionel Sambuc /*===========================================================================*
2368433d6423SLionel Sambuc * sef_local_startup *
2369433d6423SLionel Sambuc *===========================================================================*/
sef_local_startup(void)2370433d6423SLionel Sambuc static void sef_local_startup(void)
2371433d6423SLionel Sambuc {
2372433d6423SLionel Sambuc /* Set callbacks and initialize the System Event Framework (SEF).
2373433d6423SLionel Sambuc */
2374433d6423SLionel Sambuc
2375433d6423SLionel Sambuc /* Register init callbacks. */
2376433d6423SLionel Sambuc sef_setcb_init_fresh(sef_cb_init_fresh);
2377433d6423SLionel Sambuc
2378433d6423SLionel Sambuc /* Register signal callbacks. */
2379433d6423SLionel Sambuc sef_setcb_signal_handler(sef_cb_signal_handler);
2380433d6423SLionel Sambuc
23810d6c408fSDavid van Moolenbroek /* Enable support for live update. */
23820d6c408fSDavid van Moolenbroek blockdriver_mt_support_lu();
23830d6c408fSDavid van Moolenbroek
2384433d6423SLionel Sambuc /* Let SEF perform startup. */
2385433d6423SLionel Sambuc sef_startup();
2386433d6423SLionel Sambuc }
2387433d6423SLionel Sambuc
2388433d6423SLionel Sambuc /*===========================================================================*
2389433d6423SLionel Sambuc * ahci_portname *
2390433d6423SLionel Sambuc *===========================================================================*/
ahci_portname(struct port_state * ps)2391433d6423SLionel Sambuc static char *ahci_portname(struct port_state *ps)
2392433d6423SLionel Sambuc {
2393433d6423SLionel Sambuc /* Return a printable name for the given port. Whenever we can, print a
2394433d6423SLionel Sambuc * "Dx" device number rather than a "Pxx" port number, because the user
2395433d6423SLionel Sambuc * may not be aware of the mapping currently in use.
2396433d6423SLionel Sambuc */
2397433d6423SLionel Sambuc static char name[] = "AHCI0-P00";
2398433d6423SLionel Sambuc
2399433d6423SLionel Sambuc name[4] = '0' + ahci_instance;
2400433d6423SLionel Sambuc
2401433d6423SLionel Sambuc if (ps->device == NO_DEVICE) {
2402433d6423SLionel Sambuc name[6] = 'P';
2403433d6423SLionel Sambuc name[7] = '0' + (ps - port_state) / 10;
2404433d6423SLionel Sambuc name[8] = '0' + (ps - port_state) % 10;
2405433d6423SLionel Sambuc }
2406433d6423SLionel Sambuc else {
2407433d6423SLionel Sambuc name[6] = 'D';
2408433d6423SLionel Sambuc name[7] = '0' + ps->device;
2409433d6423SLionel Sambuc name[8] = 0;
2410433d6423SLionel Sambuc }
2411433d6423SLionel Sambuc
2412433d6423SLionel Sambuc return name;
2413433d6423SLionel Sambuc }
2414433d6423SLionel Sambuc
2415433d6423SLionel Sambuc /*===========================================================================*
2416433d6423SLionel Sambuc * ahci_map_minor *
2417433d6423SLionel Sambuc *===========================================================================*/
ahci_map_minor(devminor_t minor,struct device ** dvp)2418433d6423SLionel Sambuc static struct port_state *ahci_map_minor(devminor_t minor, struct device **dvp)
2419433d6423SLionel Sambuc {
2420433d6423SLionel Sambuc /* Map a minor device number to a port and a pointer to the partition's
2421433d6423SLionel Sambuc * device structure. Return NULL if this minor device number does not
2422433d6423SLionel Sambuc * identify an actual device.
2423433d6423SLionel Sambuc */
2424433d6423SLionel Sambuc struct port_state *ps;
2425433d6423SLionel Sambuc int port;
2426433d6423SLionel Sambuc
2427433d6423SLionel Sambuc ps = NULL;
2428433d6423SLionel Sambuc
2429433d6423SLionel Sambuc if (minor >= 0 && minor < NR_MINORS) {
2430433d6423SLionel Sambuc port = ahci_map[minor / DEV_PER_DRIVE];
2431433d6423SLionel Sambuc
2432433d6423SLionel Sambuc if (port == NO_PORT)
2433433d6423SLionel Sambuc return NULL;
2434433d6423SLionel Sambuc
2435433d6423SLionel Sambuc ps = &port_state[port];
2436433d6423SLionel Sambuc *dvp = &ps->part[minor % DEV_PER_DRIVE];
2437433d6423SLionel Sambuc }
2438433d6423SLionel Sambuc else if ((unsigned) (minor -= MINOR_d0p0s0) < NR_SUBDEVS) {
2439433d6423SLionel Sambuc port = ahci_map[minor / SUB_PER_DRIVE];
2440433d6423SLionel Sambuc
2441433d6423SLionel Sambuc if (port == NO_PORT)
2442433d6423SLionel Sambuc return NULL;
2443433d6423SLionel Sambuc
2444433d6423SLionel Sambuc ps = &port_state[port];
2445433d6423SLionel Sambuc *dvp = &ps->subpart[minor % SUB_PER_DRIVE];
2446433d6423SLionel Sambuc }
2447433d6423SLionel Sambuc
2448433d6423SLionel Sambuc return ps;
2449433d6423SLionel Sambuc }
2450433d6423SLionel Sambuc
2451433d6423SLionel Sambuc /*===========================================================================*
2452433d6423SLionel Sambuc * ahci_part *
2453433d6423SLionel Sambuc *===========================================================================*/
ahci_part(devminor_t minor)2454433d6423SLionel Sambuc static struct device *ahci_part(devminor_t minor)
2455433d6423SLionel Sambuc {
2456433d6423SLionel Sambuc /* Return a pointer to the partition information structure of the given
2457433d6423SLionel Sambuc * minor device.
2458433d6423SLionel Sambuc */
2459433d6423SLionel Sambuc struct device *dv;
2460433d6423SLionel Sambuc
2461433d6423SLionel Sambuc if (ahci_map_minor(minor, &dv) == NULL)
2462433d6423SLionel Sambuc return NULL;
2463433d6423SLionel Sambuc
2464433d6423SLionel Sambuc return dv;
2465433d6423SLionel Sambuc }
2466433d6423SLionel Sambuc
2467433d6423SLionel Sambuc /*===========================================================================*
2468433d6423SLionel Sambuc * ahci_open *
2469433d6423SLionel Sambuc *===========================================================================*/
ahci_open(devminor_t minor,int access)2470433d6423SLionel Sambuc static int ahci_open(devminor_t minor, int access)
2471433d6423SLionel Sambuc {
2472433d6423SLionel Sambuc /* Open a device.
2473433d6423SLionel Sambuc */
2474433d6423SLionel Sambuc struct port_state *ps;
2475433d6423SLionel Sambuc int r;
2476433d6423SLionel Sambuc
2477433d6423SLionel Sambuc ps = ahci_get_port(minor);
2478433d6423SLionel Sambuc
2479433d6423SLionel Sambuc /* Only one open request can be processed at a time, due to the fact
2480433d6423SLionel Sambuc * that it is an exclusive operation. The thread that handles this call
2481433d6423SLionel Sambuc * can therefore freely register itself at slot zero.
2482433d6423SLionel Sambuc */
2483433d6423SLionel Sambuc ps->cmd_info[0].tid = blockdriver_mt_get_tid();
2484433d6423SLionel Sambuc
2485433d6423SLionel Sambuc /* If we are still in the process of initializing this port or device,
2486433d6423SLionel Sambuc * wait for completion of that phase first.
2487433d6423SLionel Sambuc */
2488433d6423SLionel Sambuc if (ps->flags & FLAG_BUSY)
2489433d6423SLionel Sambuc port_wait(ps);
2490433d6423SLionel Sambuc
2491433d6423SLionel Sambuc /* The device may only be opened if it is now properly functioning. */
2492433d6423SLionel Sambuc if (ps->state != STATE_GOOD_DEV)
2493433d6423SLionel Sambuc return ENXIO;
2494433d6423SLionel Sambuc
2495433d6423SLionel Sambuc /* Some devices may only be opened in read-only mode. */
2496433d6423SLionel Sambuc if ((ps->flags & FLAG_READONLY) && (access & BDEV_W_BIT))
2497433d6423SLionel Sambuc return EACCES;
2498433d6423SLionel Sambuc
2499433d6423SLionel Sambuc if (ps->open_count == 0) {
2500433d6423SLionel Sambuc /* The first open request. Clear the barrier flag, if set. */
2501433d6423SLionel Sambuc ps->flags &= ~FLAG_BARRIER;
2502433d6423SLionel Sambuc
2503433d6423SLionel Sambuc /* Recheck media only when nobody is using the device. */
2504433d6423SLionel Sambuc if ((ps->flags & FLAG_ATAPI) &&
2505433d6423SLionel Sambuc (r = atapi_check_medium(ps, 0)) != OK)
2506433d6423SLionel Sambuc return r;
2507433d6423SLionel Sambuc
2508433d6423SLionel Sambuc /* After rechecking the media, the partition table must always
2509433d6423SLionel Sambuc * be read. This is also a convenient time to do it for
2510433d6423SLionel Sambuc * nonremovable devices. Start by resetting the partition
2511433d6423SLionel Sambuc * tables and setting the working size of the entire device.
2512433d6423SLionel Sambuc */
2513433d6423SLionel Sambuc memset(ps->part, 0, sizeof(ps->part));
2514433d6423SLionel Sambuc memset(ps->subpart, 0, sizeof(ps->subpart));
2515433d6423SLionel Sambuc
2516433d6423SLionel Sambuc ps->part[0].dv_size = ps->lba_count * ps->sector_size;
2517433d6423SLionel Sambuc
2518433d6423SLionel Sambuc partition(&ahci_dtab, ps->device * DEV_PER_DRIVE, P_PRIMARY,
2519433d6423SLionel Sambuc !!(ps->flags & FLAG_ATAPI));
2520433d6423SLionel Sambuc
2521433d6423SLionel Sambuc blockdriver_mt_set_workers(ps->device, ps->queue_depth);
2522433d6423SLionel Sambuc }
2523433d6423SLionel Sambuc else {
2524433d6423SLionel Sambuc /* If the barrier flag is set, deny new open requests until the
2525433d6423SLionel Sambuc * device is fully closed first.
2526433d6423SLionel Sambuc */
2527433d6423SLionel Sambuc if (ps->flags & FLAG_BARRIER)
2528433d6423SLionel Sambuc return ENXIO;
2529433d6423SLionel Sambuc }
2530433d6423SLionel Sambuc
2531433d6423SLionel Sambuc ps->open_count++;
2532433d6423SLionel Sambuc
2533433d6423SLionel Sambuc return OK;
2534433d6423SLionel Sambuc }
2535433d6423SLionel Sambuc
2536433d6423SLionel Sambuc /*===========================================================================*
2537433d6423SLionel Sambuc * ahci_close *
2538433d6423SLionel Sambuc *===========================================================================*/
ahci_close(devminor_t minor)2539433d6423SLionel Sambuc static int ahci_close(devminor_t minor)
2540433d6423SLionel Sambuc {
2541433d6423SLionel Sambuc /* Close a device.
2542433d6423SLionel Sambuc */
2543433d6423SLionel Sambuc struct port_state *ps;
2544433d6423SLionel Sambuc int port;
2545433d6423SLionel Sambuc
2546433d6423SLionel Sambuc ps = ahci_get_port(minor);
2547433d6423SLionel Sambuc
2548433d6423SLionel Sambuc /* Decrease the open count. */
2549433d6423SLionel Sambuc if (ps->open_count <= 0) {
2550433d6423SLionel Sambuc dprintf(V_ERR, ("%s: closing already-closed port\n",
2551433d6423SLionel Sambuc ahci_portname(ps)));
2552433d6423SLionel Sambuc
2553433d6423SLionel Sambuc return EINVAL;
2554433d6423SLionel Sambuc }
2555433d6423SLionel Sambuc
2556433d6423SLionel Sambuc ps->open_count--;
2557433d6423SLionel Sambuc
2558433d6423SLionel Sambuc if (ps->open_count > 0)
2559433d6423SLionel Sambuc return OK;
2560433d6423SLionel Sambuc
2561433d6423SLionel Sambuc /* The device is now fully closed. That also means that the threads for
2562433d6423SLionel Sambuc * this device are not needed anymore, so we reduce the count to one.
2563433d6423SLionel Sambuc */
2564433d6423SLionel Sambuc blockdriver_mt_set_workers(ps->device, 1);
2565433d6423SLionel Sambuc
2566433d6423SLionel Sambuc if (ps->state == STATE_GOOD_DEV && !(ps->flags & FLAG_BARRIER)) {
2567433d6423SLionel Sambuc dprintf(V_INFO, ("%s: flushing write cache\n",
2568433d6423SLionel Sambuc ahci_portname(ps)));
2569433d6423SLionel Sambuc
2570433d6423SLionel Sambuc (void) gen_flush_wcache(ps);
2571433d6423SLionel Sambuc }
2572433d6423SLionel Sambuc
2573433d6423SLionel Sambuc /* If the entire driver has been told to terminate, check whether all
2574433d6423SLionel Sambuc * devices are now closed. If so, tell libblockdriver to quit after
2575433d6423SLionel Sambuc * replying to the close request.
2576433d6423SLionel Sambuc */
2577433d6423SLionel Sambuc if (ahci_exiting) {
2578433d6423SLionel Sambuc for (port = 0; port < hba_state.nr_ports; port++)
2579433d6423SLionel Sambuc if (port_state[port].open_count > 0)
2580433d6423SLionel Sambuc break;
2581433d6423SLionel Sambuc
2582433d6423SLionel Sambuc if (port == hba_state.nr_ports) {
2583433d6423SLionel Sambuc ahci_stop();
2584433d6423SLionel Sambuc
2585433d6423SLionel Sambuc blockdriver_mt_terminate();
2586433d6423SLionel Sambuc }
2587433d6423SLionel Sambuc }
2588433d6423SLionel Sambuc
2589433d6423SLionel Sambuc return OK;
2590433d6423SLionel Sambuc }
2591433d6423SLionel Sambuc
2592433d6423SLionel Sambuc /*===========================================================================*
2593433d6423SLionel Sambuc * ahci_transfer *
2594433d6423SLionel Sambuc *===========================================================================*/
ahci_transfer(devminor_t minor,int do_write,u64_t position,endpoint_t endpt,iovec_t * iovec,unsigned int count,int flags)2595433d6423SLionel Sambuc static ssize_t ahci_transfer(devminor_t minor, int do_write, u64_t position,
2596433d6423SLionel Sambuc endpoint_t endpt, iovec_t *iovec, unsigned int count, int flags)
2597433d6423SLionel Sambuc {
2598433d6423SLionel Sambuc /* Perform data transfer on the selected device.
2599433d6423SLionel Sambuc */
2600433d6423SLionel Sambuc struct port_state *ps;
2601433d6423SLionel Sambuc struct device *dv;
2602433d6423SLionel Sambuc u64_t pos, eof;
2603433d6423SLionel Sambuc
2604433d6423SLionel Sambuc ps = ahci_get_port(minor);
2605433d6423SLionel Sambuc dv = ahci_part(minor);
2606433d6423SLionel Sambuc
2607433d6423SLionel Sambuc if (ps->state != STATE_GOOD_DEV || (ps->flags & FLAG_BARRIER))
2608433d6423SLionel Sambuc return EIO;
2609433d6423SLionel Sambuc
2610433d6423SLionel Sambuc if (count > NR_IOREQS)
2611433d6423SLionel Sambuc return EINVAL;
2612433d6423SLionel Sambuc
2613433d6423SLionel Sambuc /* Check for basic end-of-partition condition: if the start position of
2614433d6423SLionel Sambuc * the request is outside the partition, return success immediately.
2615433d6423SLionel Sambuc * The size of the request is obtained, and possibly reduced, later.
2616433d6423SLionel Sambuc */
2617433d6423SLionel Sambuc if (position >= dv->dv_size)
2618433d6423SLionel Sambuc return OK;
2619433d6423SLionel Sambuc
2620433d6423SLionel Sambuc pos = dv->dv_base + position;
2621433d6423SLionel Sambuc eof = dv->dv_base + dv->dv_size;
2622433d6423SLionel Sambuc
2623433d6423SLionel Sambuc return port_transfer(ps, pos, eof, endpt, (iovec_s_t *) iovec, count,
2624433d6423SLionel Sambuc do_write, flags);
2625433d6423SLionel Sambuc }
2626433d6423SLionel Sambuc
2627433d6423SLionel Sambuc /*===========================================================================*
2628433d6423SLionel Sambuc * ahci_ioctl *
2629433d6423SLionel Sambuc *===========================================================================*/
ahci_ioctl(devminor_t minor,unsigned long request,endpoint_t endpt,cp_grant_id_t grant,endpoint_t UNUSED (user_endpt))2630433d6423SLionel Sambuc static int ahci_ioctl(devminor_t minor, unsigned long request,
2631433d6423SLionel Sambuc endpoint_t endpt, cp_grant_id_t grant, endpoint_t UNUSED(user_endpt))
2632433d6423SLionel Sambuc {
2633433d6423SLionel Sambuc /* Process I/O control requests.
2634433d6423SLionel Sambuc */
2635433d6423SLionel Sambuc struct port_state *ps;
2636433d6423SLionel Sambuc int r, val;
2637433d6423SLionel Sambuc
2638433d6423SLionel Sambuc ps = ahci_get_port(minor);
2639433d6423SLionel Sambuc
2640433d6423SLionel Sambuc switch (request) {
2641433d6423SLionel Sambuc case DIOCEJECT:
2642433d6423SLionel Sambuc if (ps->state != STATE_GOOD_DEV || (ps->flags & FLAG_BARRIER))
2643433d6423SLionel Sambuc return EIO;
2644433d6423SLionel Sambuc
2645433d6423SLionel Sambuc if (!(ps->flags & FLAG_ATAPI))
2646433d6423SLionel Sambuc return EINVAL;
2647433d6423SLionel Sambuc
2648433d6423SLionel Sambuc return atapi_load_eject(ps, 0, FALSE /*load*/);
2649433d6423SLionel Sambuc
2650433d6423SLionel Sambuc case DIOCOPENCT:
2651433d6423SLionel Sambuc return sys_safecopyto(endpt, grant, 0,
2652433d6423SLionel Sambuc (vir_bytes) &ps->open_count, sizeof(ps->open_count));
2653433d6423SLionel Sambuc
2654433d6423SLionel Sambuc case DIOCFLUSH:
2655433d6423SLionel Sambuc if (ps->state != STATE_GOOD_DEV || (ps->flags & FLAG_BARRIER))
2656433d6423SLionel Sambuc return EIO;
2657433d6423SLionel Sambuc
2658433d6423SLionel Sambuc return gen_flush_wcache(ps);
2659433d6423SLionel Sambuc
2660433d6423SLionel Sambuc case DIOCSETWC:
2661433d6423SLionel Sambuc if (ps->state != STATE_GOOD_DEV || (ps->flags & FLAG_BARRIER))
2662433d6423SLionel Sambuc return EIO;
2663433d6423SLionel Sambuc
2664433d6423SLionel Sambuc if ((r = sys_safecopyfrom(endpt, grant, 0, (vir_bytes) &val,
2665433d6423SLionel Sambuc sizeof(val))) != OK)
2666433d6423SLionel Sambuc return r;
2667433d6423SLionel Sambuc
2668433d6423SLionel Sambuc return gen_set_wcache(ps, val);
2669433d6423SLionel Sambuc
2670433d6423SLionel Sambuc case DIOCGETWC:
2671433d6423SLionel Sambuc if (ps->state != STATE_GOOD_DEV || (ps->flags & FLAG_BARRIER))
2672433d6423SLionel Sambuc return EIO;
2673433d6423SLionel Sambuc
2674433d6423SLionel Sambuc if ((r = gen_get_wcache(ps, &val)) != OK)
2675433d6423SLionel Sambuc return r;
2676433d6423SLionel Sambuc
2677433d6423SLionel Sambuc return sys_safecopyto(endpt, grant, 0, (vir_bytes) &val,
2678433d6423SLionel Sambuc sizeof(val));
2679433d6423SLionel Sambuc }
2680433d6423SLionel Sambuc
2681433d6423SLionel Sambuc return ENOTTY;
2682433d6423SLionel Sambuc }
2683433d6423SLionel Sambuc
2684433d6423SLionel Sambuc /*===========================================================================*
2685433d6423SLionel Sambuc * ahci_device *
2686433d6423SLionel Sambuc *===========================================================================*/
ahci_device(devminor_t minor,device_id_t * id)2687433d6423SLionel Sambuc static int ahci_device(devminor_t minor, device_id_t *id)
2688433d6423SLionel Sambuc {
2689433d6423SLionel Sambuc /* Map a minor device number to a device ID.
2690433d6423SLionel Sambuc */
2691433d6423SLionel Sambuc struct port_state *ps;
2692433d6423SLionel Sambuc struct device *dv;
2693433d6423SLionel Sambuc
2694433d6423SLionel Sambuc if ((ps = ahci_map_minor(minor, &dv)) == NULL)
2695433d6423SLionel Sambuc return ENXIO;
2696433d6423SLionel Sambuc
2697433d6423SLionel Sambuc *id = ps->device;
2698433d6423SLionel Sambuc
2699433d6423SLionel Sambuc return OK;
2700433d6423SLionel Sambuc }
2701433d6423SLionel Sambuc
2702433d6423SLionel Sambuc /*===========================================================================*
2703433d6423SLionel Sambuc * ahci_get_port *
2704433d6423SLionel Sambuc *===========================================================================*/
ahci_get_port(devminor_t minor)2705433d6423SLionel Sambuc static struct port_state *ahci_get_port(devminor_t minor)
2706433d6423SLionel Sambuc {
2707433d6423SLionel Sambuc /* Get the port structure associated with the given minor device.
2708433d6423SLionel Sambuc * Called only from worker threads, so the minor device is already
2709433d6423SLionel Sambuc * guaranteed to map to a port.
2710433d6423SLionel Sambuc */
2711433d6423SLionel Sambuc struct port_state *ps;
2712433d6423SLionel Sambuc struct device *dv;
2713433d6423SLionel Sambuc
2714433d6423SLionel Sambuc if ((ps = ahci_map_minor(minor, &dv)) == NULL)
2715433d6423SLionel Sambuc panic("device mapping for minor %d disappeared", minor);
2716433d6423SLionel Sambuc
2717433d6423SLionel Sambuc return ps;
2718433d6423SLionel Sambuc }
2719433d6423SLionel Sambuc
2720433d6423SLionel Sambuc /*===========================================================================*
2721433d6423SLionel Sambuc * main *
2722433d6423SLionel Sambuc *===========================================================================*/
main(int argc,char ** argv)2723433d6423SLionel Sambuc int main(int argc, char **argv)
2724433d6423SLionel Sambuc {
2725433d6423SLionel Sambuc /* Driver task.
2726433d6423SLionel Sambuc */
2727433d6423SLionel Sambuc
2728433d6423SLionel Sambuc env_setargs(argc, argv);
2729433d6423SLionel Sambuc sef_local_startup();
2730433d6423SLionel Sambuc
2731433d6423SLionel Sambuc blockdriver_mt_task(&ahci_dtab);
2732433d6423SLionel Sambuc
2733433d6423SLionel Sambuc return 0;
2734433d6423SLionel Sambuc }
2735