AHCI - Implement parallel port scan and thread each port interrupt.
[dragonfly.git] / sys / dev / disk / ahci / ahci_dragonfly.c
CommitLineData
258223a3
MD
1/*
2 * Copyright (c) 2009 The DragonFly Project. All rights reserved.
3 *
4 * This code is derived from software contributed to The DragonFly Project
5 * by Matthew Dillon <dillon@backplane.com>
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 *
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in
15 * the documentation and/or other materials provided with the
16 * distribution.
17 * 3. Neither the name of The DragonFly Project nor the names of its
18 * contributors may be used to endorse or promote products derived
19 * from this software without specific, prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
25 * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26 * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
27 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
29 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
30 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
31 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32 * SUCH DAMAGE.
33 */
34/*
35 * Primary device and CAM interface to OpenBSD AHCI driver, for DragonFly
36 */
37
38#include "ahci.h"
39
40/*
41 * Device bus methods
42 */
43
44static int ahci_probe (device_t dev);
45static int ahci_attach (device_t dev);
46static int ahci_detach (device_t dev);
47#if 0
48static int ahci_shutdown (device_t dev);
49static int ahci_suspend (device_t dev);
50static int ahci_resume (device_t dev);
51#endif
52
f4553de1
MD
53static void ahci_port_thread(void *arg);
54
258223a3
MD
55static device_method_t ahci_methods[] = {
56 DEVMETHOD(device_probe, ahci_probe),
57 DEVMETHOD(device_attach, ahci_attach),
58 DEVMETHOD(device_detach, ahci_detach),
59#if 0
60 DEVMETHOD(device_shutdown, ahci_shutdown),
61 DEVMETHOD(device_suspend, ahci_suspend),
62 DEVMETHOD(device_resume, ahci_resume),
63#endif
64
65 DEVMETHOD(bus_print_child, bus_generic_print_child),
66 DEVMETHOD(bus_driver_added, bus_generic_driver_added),
67 {0, 0}
68};
69
70static devclass_t ahci_devclass;
71
72static driver_t ahci_driver = {
73 "ahci",
74 ahci_methods,
75 sizeof(struct ahci_softc)
76};
77
78MODULE_DEPEND(ahci, cam, 1, 1, 1);
79DRIVER_MODULE(ahci, pci, ahci_driver, ahci_devclass, 0, 0);
80
81/*
82 * Device bus method procedures
83 */
84static int
85ahci_probe (device_t dev)
86{
87 const struct ahci_device *ad;
88
89 ad = ahci_lookup_device(dev);
90 if (ad) {
91 device_set_desc(dev, ad->name);
92 return(-5); /* higher priority the NATA */
93 }
94 return(ENXIO);
95}
96
97static int
98ahci_attach (device_t dev)
99{
100 struct ahci_softc *sc = device_get_softc(dev);
101 int error;
102
103 sc->sc_ad = ahci_lookup_device(dev);
104 if (sc->sc_ad == NULL)
105 return(ENXIO);
106 error = sc->sc_ad->ad_attach(dev);
107 return (error);
108}
109
110static int
111ahci_detach (device_t dev)
112{
113 struct ahci_softc *sc = device_get_softc(dev);
114 int error = 0;
115
116 if (sc->sc_ad) {
117 error = sc->sc_ad->ad_detach(dev);
118 sc->sc_ad = NULL;
119 }
120 return(error);
121}
122
123#if 0
124
125static int
126ahci_shutdown (device_t dev)
127{
128 return (0);
129}
130
131static int
132ahci_suspend (device_t dev)
133{
134 return (0);
135}
136
137static int
138ahci_resume (device_t dev)
139{
140 return (0);
141}
142
143#endif
3209f581
MD
144
145void
146ahci_os_sleep(int ms)
147{
148 int ticks;
149
150#if 0
151 if (mygd->gd_intr_nesting_level) {
152 DELAY(ms * 1000);
153 } else {
154#endif
155 {
156 ticks = hz * ms / 1000 + 1;
157 tsleep(&ticks, 0, "ahslp", ticks);
158 }
159}
f4553de1
MD
160
161/*
162 * Create the OS-specific port helper thread and per-port lock.
163 */
164void
165ahci_os_start_port(struct ahci_port *ap)
166{
167 atomic_set_int(&ap->ap_signal, AP_SIGF_INIT);
168 lockinit(&ap->ap_lock, "ahcipo", 0, 0);
169 kthread_create(ahci_port_thread, ap, &ap->ap_thread,
170 "%s", PORTNAME(ap));
171}
172
173/*
174 * Stop the OS-specific port helper thread and kill the per-port lock.
175 */
176void
177ahci_os_stop_port(struct ahci_port *ap)
178{
179 if (ap->ap_thread) {
180 ahci_os_signal_port_thread(ap, AP_SIGF_STOP);
181 ahci_os_sleep(10);
182 if (ap->ap_thread) {
183 kprintf("%s: Waiting for thread to terminate\n",
184 PORTNAME(ap));
185 while (ap->ap_thread)
186 ahci_os_sleep(100);
187 kprintf("%s: thread terminated\n",
188 PORTNAME(ap));
189 }
190 }
191 lockuninit(&ap->ap_lock);
192}
193
194/*
195 * Add (mask) to the set of bits being sent to the per-port thread helper
196 * and wake the helper up if necessary.
197 */
198void
199ahci_os_signal_port_thread(struct ahci_port *ap, int mask)
200{
201 atomic_set_int(&ap->ap_signal, mask);
202 wakeup(&ap->ap_thread);
203}
204
205/*
206 * Unconditionally lock the port structure for access.
207 */
208void
209ahci_os_lock_port(struct ahci_port *ap)
210{
211 lockmgr(&ap->ap_lock, LK_EXCLUSIVE);
212}
213
214/*
215 * Conditionally lock the port structure for access.
216 *
217 * Returns 0 on success, non-zero on failure.
218 */
219int
220ahci_os_lock_port_nb(struct ahci_port *ap)
221{
222 return (lockmgr(&ap->ap_lock, LK_EXCLUSIVE | LK_NOWAIT));
223}
224
225/*
226 * Unlock a previously locked port.
227 */
228void
229ahci_os_unlock_port(struct ahci_port *ap)
230{
231 lockmgr(&ap->ap_lock, LK_RELEASE);
232}
233
234/*
235 * Per-port thread helper. This helper thread is responsible for
236 * atomically retrieving and clearing the signal mask and calling
237 * the machine-independant driver core.
238 */
239static
240void
241ahci_port_thread(void *arg)
242{
243 struct ahci_port *ap = arg;
244 int mask;
245
246 /*
247 * The helper thread is responsible for the initial port init,
248 * so all the ports can be inited in parallel.
249 *
250 * We also run the state machine which should do all probes.
251 * Since CAM is not attached yet we will not get out-of-order
252 * SCSI attachments.
253 */
254 ahci_os_lock_port(ap);
255 ahci_port_init(ap, NULL);
256 ahci_port_state_machine(ap);
257 ahci_os_unlock_port(ap);
258 atomic_clear_int(&ap->ap_signal, AP_SIGF_INIT);
259 wakeup(&ap->ap_signal);
260
261 /*
262 * Then loop on the helper core.
263 */
264 mask = ap->ap_signal;
265 while ((mask & AP_SIGF_STOP) == 0) {
266 atomic_clear_int(&ap->ap_signal, mask);
267 ahci_port_thread_core(ap, mask);
268 crit_enter();
269 tsleep_interlock(&ap->ap_thread);
270 if (ap->ap_signal == 0)
271 tsleep(&ap->ap_thread, 0, "ahport", 0);
272 crit_exit();
273 mask = ap->ap_signal;
274 }
275 ap->ap_thread = NULL;
276}