1130391Sle/*
2130391Sle * Copyright (c) 2010, LSI Corp.
3130391Sle * All rights reserved.
4130391Sle * Author : Manjunath Ranganathaiah
5130391Sle * Support: freebsdraid@lsi.com
6130391Sle *
7130391Sle * Redistribution and use in source and binary forms, with or without
8130391Sle * modification, are permitted provided that the following conditions
9130391Sle * are met:
10130391Sle *
11130391Sle * 1. Redistributions of source code must retain the above copyright
12130391Sle *    notice, this list of conditions and the following disclaimer.
13130391Sle * 2. Redistributions in binary form must reproduce the above copyright
14130391Sle *    notice, this list of conditions and the following disclaimer in
15130391Sle *    the documentation and/or other materials provided with the
16130391Sle *    distribution.
17130391Sle * 3. Neither the name of the <ORGANIZATION> nor the names of its
18130391Sle *    contributors may be used to endorse or promote products derived
19130391Sle *    from this software without specific prior written permission.
20130391Sle *
21130391Sle * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22130391Sle * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23130391Sle * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24130391Sle * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
25130391Sle * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26130391Sle * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
27130391Sle * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28130391Sle * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
29130391Sle * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30130391Sle * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
31130391Sle * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
32130391Sle * POSSIBILITY OF SUCH DAMAGE.
33130391Sle */
34130391Sle
35130391Sle#include <sys/cdefs.h>
36130391Sle__FBSDID("$FreeBSD$");
37130391Sle
38130391Sle#include <dev/tws/tws.h>
39130391Sle#include <dev/tws/tws_services.h>
40130391Sle#include <dev/tws/tws_hdm.h>
41130391Sle
42130391Sle#include <cam/cam.h>
43130391Sle#include <cam/cam_ccb.h>
44130391Sle
45130391SleMALLOC_DEFINE(M_TWS, "twsbuf", "buffers used by tws driver");
46130391Sleint tws_queue_depth = TWS_MAX_REQS;
47130391Sleint tws_enable_msi = 0;
48130391Sleint tws_enable_msix = 0;
49130391Sle
50130391Sle
51130391Sle
52130391Sle/* externs */
53130391Sleextern int tws_cam_attach(struct tws_softc *sc);
54130391Sleextern void tws_cam_detach(struct tws_softc *sc);
55130391Sleextern int tws_init_ctlr(struct tws_softc *sc);
56130391Sleextern boolean tws_ctlr_ready(struct tws_softc *sc);
57130391Sleextern void tws_turn_off_interrupts(struct tws_softc *sc);
58138110Sleextern void tws_q_insert_tail(struct tws_softc *sc, struct tws_request *req,
59130391Sle                                u_int8_t q_type );
60130391Sleextern struct tws_request *tws_q_remove_request(struct tws_softc *sc,
61130391Sle                                   struct tws_request *req, u_int8_t q_type );
62138112Sleextern struct tws_request *tws_q_remove_head(struct tws_softc *sc,
63130391Sle                                                       u_int8_t q_type );
64130391Sleextern boolean tws_get_response(struct tws_softc *sc, u_int16_t *req_id);
65130391Sleextern boolean tws_ctlr_reset(struct tws_softc *sc);
66130391Sleextern void tws_intr(void *arg);
67130391Sleextern int tws_use_32bit_sgls;
68130391Sle
69130391Sle
70130391Slestruct tws_request *tws_get_request(struct tws_softc *sc, u_int16_t type);
71130391Sleint tws_init_connect(struct tws_softc *sc, u_int16_t mc);
72130391Slevoid tws_send_event(struct tws_softc *sc, u_int8_t event);
73130391Sleuint8_t tws_get_state(struct tws_softc *sc);
74130391Slevoid tws_release_request(struct tws_request *req);
75130391Sle
76130391Sle
77130391Sle
78130391Sle/* Function prototypes */
79130391Slestatic d_open_t     tws_open;
80130391Slestatic d_close_t    tws_close;
81130391Slestatic d_read_t     tws_read;
82130391Slestatic d_write_t    tws_write;
83130391Sleextern d_ioctl_t    tws_ioctl;
84130391Sle
85130391Slestatic int tws_init(struct tws_softc *sc);
86130391Slestatic void tws_dmamap_cmds_load_cbfn(void *arg, bus_dma_segment_t *segs,
87130391Sle                           int nseg, int error);
88130391Sle
89130391Slestatic int tws_init_reqs(struct tws_softc *sc, u_int32_t dma_mem_size);
90130391Slestatic int tws_init_aen_q(struct tws_softc *sc);
91130391Slestatic int tws_init_trace_q(struct tws_softc *sc);
92130391Slestatic int tws_setup_irq(struct tws_softc *sc);
93130391Sleint tws_setup_intr(struct tws_softc *sc, int irqs);
94130391Sleint tws_teardown_intr(struct tws_softc *sc);
95130391Sle
96130391Sle
97130391Sle/* Character device entry points */
98130391Sle
99130391Slestatic struct cdevsw tws_cdevsw = {
100130391Sle    .d_version =    D_VERSION,
101130391Sle    .d_open =   tws_open,
102130391Sle    .d_close =  tws_close,
103130391Sle    .d_read =   tws_read,
104130391Sle    .d_write =  tws_write,
105130391Sle    .d_ioctl =  tws_ioctl,
106130391Sle    .d_name =   "tws",
107130391Sle};
108130391Sle
109130391Sle/*
110130391Sle * In the cdevsw routines, we find our softc by using the si_drv1 member
111130391Sle * of struct cdev.  We set this variable to point to our softc in our
112130391Sle * attach routine when we create the /dev entry.
113130391Sle */
114130391Sle
115130391Sleint
116130391Sletws_open(struct cdev *dev, int oflags, int devtype, d_thread_t *td)
117130391Sle{
118130391Sle    struct tws_softc *sc = dev->si_drv1;
119130391Sle
120130391Sle    if ( sc )
121130391Sle        TWS_TRACE_DEBUG(sc, "entry", dev, oflags);
122130391Sle    return (0);
123130391Sle}
124130391Sle
125130391Sleint
126130391Sletws_close(struct cdev *dev, int fflag, int devtype, d_thread_t *td)
127130391Sle{
128130391Sle    struct tws_softc *sc = dev->si_drv1;
129130391Sle
130130391Sle    if ( sc )
131130391Sle        TWS_TRACE_DEBUG(sc, "entry", dev, fflag);
132130391Sle    return (0);
133130391Sle}
134130391Sle
135130391Sleint
136130391Sletws_read(struct cdev *dev, struct uio *uio, int ioflag)
137130391Sle{
138130391Sle    struct tws_softc *sc = dev->si_drv1;
139130391Sle
140130391Sle    if ( sc )
141130391Sle        TWS_TRACE_DEBUG(sc, "entry", dev, ioflag);
142130391Sle    return (0);
143130391Sle}
144130391Sle
145130391Sleint
146130391Sletws_write(struct cdev *dev, struct uio *uio, int ioflag)
147130391Sle{
148130391Sle    struct tws_softc *sc = dev->si_drv1;
149130391Sle
150130391Sle    if ( sc )
151130391Sle        TWS_TRACE_DEBUG(sc, "entry", dev, ioflag);
152130391Sle    return (0);
153130391Sle}
154130391Sle
155130391Sle/* PCI Support Functions */
156130391Sle
157130391Sle/*
158130391Sle * Compare the device ID of this device against the IDs that this driver
159130391Sle * supports.  If there is a match, set the description and return success.
160133097Sle */
161133097Slestatic int
162133097Sletws_probe(device_t dev)
163133097Sle{
164133097Sle    static u_int8_t first_ctlr = 1;
165133097Sle
166133097Sle    if ((pci_get_vendor(dev) == TWS_VENDOR_ID) &&
167133097Sle        (pci_get_device(dev) == TWS_DEVICE_ID)) {
168133097Sle        device_set_desc(dev, "LSI 3ware SAS/SATA Storage Controller");
169133097Sle        if (first_ctlr) {
170133097Sle            printf("LSI 3ware device driver for SAS/SATA storage "
171133097Sle                    "controllers, version: %s\n", TWS_DRIVER_VERSION_STRING);
172133097Sle            first_ctlr = 0;
173133097Sle        }
174133097Sle
175133097Sle        return(BUS_PROBE_DEFAULT);
176133097Sle    }
177133097Sle    return (ENXIO);
178133097Sle}
179133097Sle
180133097Sle/* Attach function is only called if the probe is successful. */
181133097Sle
182133097Slestatic int
183133097Sletws_attach(device_t dev)
184133097Sle{
185133097Sle    struct tws_softc *sc = device_get_softc(dev);
186133097Sle    u_int32_t bar;
187133097Sle    int error=0,i;
188133097Sle
189133097Sle    /* no tracing yet */
190133097Sle    /* Look up our softc and initialize its fields. */
191133097Sle    sc->tws_dev = dev;
192133097Sle    sc->device_id = pci_get_device(dev);
193133097Sle    sc->subvendor_id = pci_get_subvendor(dev);
194133097Sle    sc->subdevice_id = pci_get_subdevice(dev);
195130391Sle
196130391Sle    /* Intialize mutexes */
197130391Sle    mtx_init( &sc->q_lock, "tws_q_lock", NULL, MTX_DEF);
198130391Sle    mtx_init( &sc->sim_lock,  "tws_sim_lock", NULL, MTX_DEF);
199130391Sle    mtx_init( &sc->gen_lock,  "tws_gen_lock", NULL, MTX_DEF);
200130391Sle    mtx_init( &sc->io_lock,  "tws_io_lock", NULL, MTX_DEF | MTX_RECURSE);
201130391Sle
202130391Sle    if ( tws_init_trace_q(sc) == FAILURE )
203130391Sle        printf("trace init failure\n");
204130391Sle    /* send init event */
205130391Sle    mtx_lock(&sc->gen_lock);
206130391Sle    tws_send_event(sc, TWS_INIT_START);
207130391Sle    mtx_unlock(&sc->gen_lock);
208130391Sle
209130391Sle
210130391Sle#if _BYTE_ORDER == _BIG_ENDIAN
211130391Sle    TWS_TRACE(sc, "BIG endian", 0, 0);
212130391Sle#endif
213130391Sle    /* sysctl context setup */
214130391Sle    sysctl_ctx_init(&sc->tws_clist);
215130391Sle    sc->tws_oidp = SYSCTL_ADD_NODE(&sc->tws_clist,
216130391Sle                                   SYSCTL_STATIC_CHILDREN(_hw), OID_AUTO,
217130391Sle                                   device_get_nameunit(dev),
218130391Sle                                   CTLFLAG_RD, 0, "");
219130391Sle    if ( sc->tws_oidp == NULL ) {
220130391Sle        tws_log(sc, SYSCTL_TREE_NODE_ADD);
221130391Sle        goto attach_fail_1;
222130391Sle    }
223130391Sle    SYSCTL_ADD_STRING(&sc->tws_clist, SYSCTL_CHILDREN(sc->tws_oidp),
224130391Sle                      OID_AUTO, "driver_version", CTLFLAG_RD,
225130391Sle                      TWS_DRIVER_VERSION_STRING, 0, "TWS driver version");
226130391Sle
227130391Sle    pci_enable_busmaster(dev);
228130391Sle
229130391Sle    bar = pci_read_config(dev, TWS_PCI_BAR0, 4);
230130391Sle    TWS_TRACE_DEBUG(sc, "bar0 ", bar, 0);
231130391Sle    bar = pci_read_config(dev, TWS_PCI_BAR1, 4);
232130391Sle    bar = bar & ~TWS_BIT2;
233130391Sle    TWS_TRACE_DEBUG(sc, "bar1 ", bar, 0);
234130391Sle
235130391Sle    /* MFA base address is BAR2 register used for
236130391Sle     * push mode. Firmware will evatualy move to
237130391Sle     * pull mode during witch this needs to change
238130391Sle     */
239130391Sle#ifndef TWS_PULL_MODE_ENABLE
240130391Sle    sc->mfa_base = (u_int64_t)pci_read_config(dev, TWS_PCI_BAR2, 4);
241130391Sle    sc->mfa_base = sc->mfa_base & ~TWS_BIT2;
242130391Sle    TWS_TRACE_DEBUG(sc, "bar2 ", sc->mfa_base, 0);
243130391Sle#endif
244130391Sle
245130391Sle    /* allocate MMIO register space */
246130391Sle    sc->reg_res_id = TWS_PCI_BAR1; /* BAR1 offset */
247130391Sle    if ((sc->reg_res = bus_alloc_resource(dev, SYS_RES_MEMORY,
248130391Sle                                &(sc->reg_res_id), 0, ~0, 1, RF_ACTIVE))
249130391Sle                                == NULL) {
250130391Sle        tws_log(sc, ALLOC_MEMORY_RES);
251130391Sle        goto attach_fail_1;
252130391Sle    }
253130391Sle    sc->bus_tag = rman_get_bustag(sc->reg_res);
254130391Sle    sc->bus_handle = rman_get_bushandle(sc->reg_res);
255130391Sle
256130391Sle#ifndef TWS_PULL_MODE_ENABLE
257130391Sle    /* Allocate bus space for inbound mfa */
258130391Sle    sc->mfa_res_id = TWS_PCI_BAR2; /* BAR2 offset */
259130391Sle    if ((sc->mfa_res = bus_alloc_resource(dev, SYS_RES_MEMORY,
260130391Sle                          &(sc->mfa_res_id), 0, ~0, 0x100000, RF_ACTIVE))
261130391Sle                                == NULL) {
262130391Sle        tws_log(sc, ALLOC_MEMORY_RES);
263130391Sle        goto attach_fail_2;
264130391Sle    }
265130391Sle    sc->bus_mfa_tag = rman_get_bustag(sc->mfa_res);
266130391Sle    sc->bus_mfa_handle = rman_get_bushandle(sc->mfa_res);
267130391Sle#endif
268130391Sle
269130391Sle    /* Allocate and register our interrupt. */
270130391Sle    sc->intr_type = TWS_INTx; /* default */
271130391Sle
272130391Sle    if ( tws_enable_msi )
273130391Sle        sc->intr_type = TWS_MSI;
274130391Sle    if ( tws_setup_irq(sc) == FAILURE ) {
275130391Sle        tws_log(sc, ALLOC_MEMORY_RES);
276130391Sle        goto attach_fail_3;
277130391Sle    }
278130391Sle
279130391Sle    /*
280130391Sle     * Create a /dev entry for this device.  The kernel will assign us
281130391Sle     * a major number automatically.  We use the unit number of this
282130391Sle     * device as the minor number and name the character device
283130391Sle     * "tws<unit>".
284130391Sle     */
285130391Sle    sc->tws_cdev = make_dev(&tws_cdevsw, device_get_unit(dev),
286130391Sle        UID_ROOT, GID_OPERATOR, S_IRUSR | S_IWUSR, "tws%u",
287130391Sle        device_get_unit(dev));
288130391Sle    sc->tws_cdev->si_drv1 = sc;
289130391Sle
290130391Sle    if ( tws_init(sc) == FAILURE ) {
291130391Sle        tws_log(sc, TWS_INIT_FAILURE);
292130391Sle        goto attach_fail_4;
293130391Sle    }
294130391Sle    if ( tws_init_ctlr(sc) == FAILURE ) {
295130391Sle        tws_log(sc, TWS_CTLR_INIT_FAILURE);
296130391Sle        goto attach_fail_4;
297130391Sle    }
298130391Sle    if ((error = tws_cam_attach(sc))) {
299130391Sle        tws_log(sc, TWS_CAM_ATTACH);
300130391Sle        goto attach_fail_4;
301130391Sle    }
302130391Sle    /* send init complete event */
303130391Sle    mtx_lock(&sc->gen_lock);
304130391Sle    tws_send_event(sc, TWS_INIT_COMPLETE);
305130391Sle    mtx_unlock(&sc->gen_lock);
306130391Sle
307130391Sle    TWS_TRACE_DEBUG(sc, "attached successfully", 0, sc->device_id);
308130391Sle    return(0);
309130391Sle
310130391Sleattach_fail_4:
311130391Sle    tws_teardown_intr(sc);
312130391Sle    destroy_dev(sc->tws_cdev);
313130391Sleattach_fail_3:
314130391Sle    for(i=0;i<sc->irqs;i++) {
315130391Sle        if ( sc->irq_res[i] ){
316130391Sle            if (bus_release_resource(sc->tws_dev,
317130391Sle                 SYS_RES_IRQ, sc->irq_res_id[i], sc->irq_res[i]))
318130391Sle                TWS_TRACE(sc, "bus irq res", 0, 0);
319130391Sle        }
320130391Sle    }
321130391Sle#ifndef TWS_PULL_MODE_ENABLE
322130391Sleattach_fail_2:
323130391Sle#endif
324130391Sle    if ( sc->mfa_res ){
325130391Sle        if (bus_release_resource(sc->tws_dev,
326130391Sle                 SYS_RES_MEMORY, sc->mfa_res_id, sc->mfa_res))
327130391Sle            TWS_TRACE(sc, "bus release ", 0, sc->mfa_res_id);
328130391Sle    }
329130391Sle    if ( sc->reg_res ){
330130391Sle        if (bus_release_resource(sc->tws_dev,
331130391Sle                 SYS_RES_MEMORY, sc->reg_res_id, sc->reg_res))
332130391Sle            TWS_TRACE(sc, "bus release2 ", 0, sc->reg_res_id);
333130391Sle    }
334130391Sleattach_fail_1:
335130391Sle    mtx_destroy(&sc->q_lock);
336130391Sle    mtx_destroy(&sc->sim_lock);
337130391Sle    mtx_destroy(&sc->gen_lock);
338130391Sle    mtx_destroy(&sc->io_lock);
339130391Sle    sysctl_ctx_free(&sc->tws_clist);
340130391Sle    return (ENXIO);
341130391Sle}
342130391Sle
343130391Sle/* Detach device. */
344130391Sle
345130391Slestatic int
346130391Sletws_detach(device_t dev)
347130391Sle{
348130391Sle    struct tws_softc *sc = device_get_softc(dev);
349130391Sle    int i;
350130391Sle    u_int32_t reg;
351130391Sle
352130391Sle    TWS_TRACE_DEBUG(sc, "entry", 0, 0);
353130391Sle
354130391Sle    mtx_lock(&sc->gen_lock);
355130391Sle    tws_send_event(sc, TWS_UNINIT_START);
356130391Sle    mtx_unlock(&sc->gen_lock);
357130391Sle
358130391Sle    /* needs to disable interrupt before detaching from cam */
359130391Sle    tws_turn_off_interrupts(sc);
360130391Sle    /* clear door bell */
361130391Sle    tws_write_reg(sc, TWS_I2O0_HOBDBC, ~0, 4);
362130391Sle    reg = tws_read_reg(sc, TWS_I2O0_HIMASK, 4);
363130391Sle    TWS_TRACE_DEBUG(sc, "turn-off-intr", reg, 0);
364130391Sle    sc->obfl_q_overrun = false;
365130391Sle    tws_init_connect(sc, 1);
366130391Sle
367130391Sle    /* Teardown the state in our softc created in our attach routine. */
368130391Sle    /* Disconnect the interrupt handler. */
369130391Sle    tws_teardown_intr(sc);
370130391Sle
371130391Sle    /* Release irq resource */
372130391Sle    for(i=0;i<sc->irqs;i++) {
373130391Sle        if ( sc->irq_res[i] ){
374130391Sle            if (bus_release_resource(sc->tws_dev,
375130391Sle                     SYS_RES_IRQ, sc->irq_res_id[i], sc->irq_res[i]))
376130391Sle                TWS_TRACE(sc, "bus release irq resource",
377130391Sle                                       i, sc->irq_res_id[i]);
378130391Sle        }
379130391Sle    }
380130391Sle    if ( sc->intr_type == TWS_MSI ) {
381130391Sle        pci_release_msi(sc->tws_dev);
382130391Sle    }
383130391Sle
384130391Sle    tws_cam_detach(sc);
385130391Sle
386130391Sle    /* Release memory resource */
387130391Sle    if ( sc->mfa_res ){
388130391Sle        if (bus_release_resource(sc->tws_dev,
389130391Sle                 SYS_RES_MEMORY, sc->mfa_res_id, sc->mfa_res))
390130391Sle            TWS_TRACE(sc, "bus release mem resource", 0, sc->mfa_res_id);
391130391Sle    }
392130391Sle    if ( sc->reg_res ){
393130391Sle        if (bus_release_resource(sc->tws_dev,
394130391Sle                 SYS_RES_MEMORY, sc->reg_res_id, sc->reg_res))
395130391Sle            TWS_TRACE(sc, "bus release mem resource", 0, sc->reg_res_id);
396130391Sle    }
397130391Sle
398130391Sle    free(sc->reqs, M_TWS);
399130391Sle    free(sc->sense_bufs, M_TWS);
400130391Sle    free(sc->scan_ccb, M_TWS);
401130391Sle    if (sc->ioctl_data_mem)
402130391Sle            bus_dmamem_free(sc->data_tag, sc->ioctl_data_mem, sc->ioctl_data_map);
403130391Sle    free(sc->aen_q.q, M_TWS);
404130391Sle    free(sc->trace_q.q, M_TWS);
405130391Sle    mtx_destroy(&sc->q_lock);
406130391Sle    mtx_destroy(&sc->sim_lock);
407130391Sle    mtx_destroy(&sc->gen_lock);
408130391Sle    mtx_destroy(&sc->io_lock);
409130391Sle    destroy_dev(sc->tws_cdev);
410130391Sle    sysctl_ctx_free(&sc->tws_clist);
411130391Sle    return (0);
412130391Sle}
413130391Sle
414130391Sleint
415130391Sletws_setup_intr(struct tws_softc *sc, int irqs)
416130391Sle{
417130391Sle    int i, error;
418130391Sle
419130391Sle    for(i=0;i<irqs;i++) {
420130391Sle        if (!(sc->intr_handle[i])) {
421130391Sle            if ((error = bus_setup_intr(sc->tws_dev, sc->irq_res[i],
422130391Sle                                    INTR_TYPE_CAM | INTR_MPSAFE,
423130391Sle#if (__FreeBSD_version >= 700000)
424130391Sle                                    NULL,
425130391Sle#endif
426130391Sle                                    tws_intr, sc, &sc->intr_handle[i]))) {
427130391Sle                tws_log(sc, SETUP_INTR_RES);
428130391Sle                return(FAILURE);
429130391Sle            }
430130391Sle        }
431130391Sle    }
432130391Sle    return(SUCCESS);
433130391Sle
434130391Sle}
435130391Sle
436130391Sle
437130391Sleint
438130391Sletws_teardown_intr(struct tws_softc *sc)
439130391Sle{
440130391Sle    int i, error;
441130391Sle
442130391Sle    for(i=0;i<sc->irqs;i++) {
443130391Sle        if (sc->intr_handle[i]) {
444130391Sle            error = bus_teardown_intr(sc->tws_dev,
445130391Sle                                      sc->irq_res[i], sc->intr_handle[i]);
446130391Sle            sc->intr_handle[i] = NULL;
447130391Sle        }
448130391Sle    }
449130391Sle    return(SUCCESS);
450130391Sle}
451130391Sle
452130391Sle
453130391Slestatic int
454130391Sletws_setup_irq(struct tws_softc *sc)
455130391Sle{
456130391Sle    int messages;
457130391Sle
458130391Sle    switch(sc->intr_type) {
459130391Sle        case TWS_INTx :
460130391Sle            sc->irqs = 1;
461130391Sle            sc->irq_res_id[0] = 0;
462130391Sle            sc->irq_res[0] = bus_alloc_resource_any(sc->tws_dev, SYS_RES_IRQ,
463130391Sle                            &sc->irq_res_id[0], RF_SHAREABLE | RF_ACTIVE);
464130391Sle            if ( ! sc->irq_res[0] )
465130391Sle                return(FAILURE);
466130391Sle            if ( tws_setup_intr(sc, sc->irqs) == FAILURE )
467130391Sle                return(FAILURE);
468130391Sle            device_printf(sc->tws_dev, "Using legacy INTx\n");
469130391Sle            break;
470130391Sle        case TWS_MSI :
471130391Sle            sc->irqs = 1;
472130391Sle            sc->irq_res_id[0] = 1;
473130391Sle            messages = 1;
474130391Sle            if (pci_alloc_msi(sc->tws_dev, &messages) != 0 ) {
475130391Sle                TWS_TRACE(sc, "pci alloc msi fail", 0, messages);
476130391Sle                return(FAILURE);
477130391Sle            }
478130391Sle            sc->irq_res[0] = bus_alloc_resource_any(sc->tws_dev, SYS_RES_IRQ,
479130391Sle                              &sc->irq_res_id[0], RF_SHAREABLE | RF_ACTIVE);
480130391Sle
481130391Sle            if ( !sc->irq_res[0]  )
482130391Sle                return(FAILURE);
483130391Sle            if ( tws_setup_intr(sc, sc->irqs) == FAILURE )
484130391Sle                return(FAILURE);
485130391Sle            device_printf(sc->tws_dev, "Using MSI\n");
486138112Sle            break;
487138112Sle
488138112Sle    }
489138112Sle
490138112Sle    return(SUCCESS);
491138112Sle}
492138112Sle
493138112Slestatic int
494138112Sletws_init(struct tws_softc *sc)
495138112Sle{
496138112Sle
497138112Sle    u_int32_t max_sg_elements;
498138112Sle    u_int32_t dma_mem_size;
499138112Sle    int error;
500138112Sle    u_int32_t reg;
501138112Sle
502138112Sle    sc->seq_id = 0;
503138112Sle    if ( tws_queue_depth > TWS_MAX_REQS )
504138112Sle        tws_queue_depth = TWS_MAX_REQS;
505138112Sle    if (tws_queue_depth < TWS_RESERVED_REQS+1)
506138112Sle        tws_queue_depth = TWS_RESERVED_REQS+1;
507138112Sle    sc->is64bit = (sizeof(bus_addr_t) == 8) ? true : false;
508138112Sle    max_sg_elements = (sc->is64bit && !tws_use_32bit_sgls) ?
509138112Sle                                 TWS_MAX_64BIT_SG_ELEMENTS :
510138112Sle                                 TWS_MAX_32BIT_SG_ELEMENTS;
511138112Sle    dma_mem_size = (sizeof(struct tws_command_packet) * tws_queue_depth) +
512138112Sle                             (TWS_SECTOR_SIZE) ;
513138112Sle    if ( bus_dma_tag_create(bus_get_dma_tag(sc->tws_dev), /* PCI parent */
514138112Sle                            TWS_ALIGNMENT,           /* alignment */
515138112Sle                            0,                       /* boundary */
516138112Sle                            BUS_SPACE_MAXADDR_32BIT, /* lowaddr */
517138112Sle                            BUS_SPACE_MAXADDR,       /* highaddr */
518138112Sle                            NULL, NULL,              /* filter, filterarg */
519138112Sle                            BUS_SPACE_MAXSIZE,       /* maxsize */
520138112Sle                            max_sg_elements,         /* numsegs */
521138112Sle                            BUS_SPACE_MAXSIZE,       /* maxsegsize */
522138112Sle                            0,                       /* flags */
523138112Sle                            NULL, NULL,              /* lockfunc, lockfuncarg */
524138112Sle                            &sc->parent_tag          /* tag */
525138112Sle                           )) {
526138112Sle        TWS_TRACE_DEBUG(sc, "DMA parent tag Create fail", max_sg_elements,
527138112Sle                                                    sc->is64bit);
528138112Sle        return(ENOMEM);
529138112Sle    }
530138112Sle    /* In bound message frame requires 16byte alignment.
531138112Sle     * Outbound MF's can live with 4byte alignment - for now just
532138112Sle     * use 16 for both.
533138112Sle     */
534138112Sle    if ( bus_dma_tag_create(sc->parent_tag,       /* parent */
535138112Sle                            TWS_IN_MF_ALIGNMENT,  /* alignment */
536138112Sle                            0,                    /* boundary */
537138112Sle                            BUS_SPACE_MAXADDR_32BIT, /* lowaddr */
538138112Sle                            BUS_SPACE_MAXADDR,    /* highaddr */
539138112Sle                            NULL, NULL,           /* filter, filterarg */
540138112Sle                            dma_mem_size,         /* maxsize */
541130391Sle                            1,                    /* numsegs */
542130391Sle                            BUS_SPACE_MAXSIZE,    /* maxsegsize */
543130391Sle                            0,                    /* flags */
544130391Sle                            NULL, NULL,           /* lockfunc, lockfuncarg */
545130391Sle                            &sc->cmd_tag          /* tag */
546130391Sle                           )) {
547130391Sle        TWS_TRACE_DEBUG(sc, "DMA cmd tag Create fail", max_sg_elements, sc->is64bit);
548130391Sle        return(ENOMEM);
549130391Sle    }
550130391Sle
551130391Sle    if (bus_dmamem_alloc(sc->cmd_tag, &sc->dma_mem,
552130391Sle                    BUS_DMA_NOWAIT, &sc->cmd_map)) {
553130391Sle        TWS_TRACE_DEBUG(sc, "DMA mem alloc fail", max_sg_elements, sc->is64bit);
554130391Sle        return(ENOMEM);
555130391Sle    }
556130391Sle
557130391Sle    /* if bus_dmamem_alloc succeeds then bus_dmamap_load will succeed */
558130391Sle    sc->dma_mem_phys=0;
559130391Sle    error = bus_dmamap_load(sc->cmd_tag, sc->cmd_map, sc->dma_mem,
560130391Sle                    dma_mem_size, tws_dmamap_cmds_load_cbfn,
561130391Sle                    &sc->dma_mem_phys, 0);
562130391Sle
563130391Sle   /*
564130391Sle    * Create a dma tag for data buffers; size will be the maximum
565130391Sle    * possible I/O size (128kB).
566130391Sle    */
567130391Sle    if (bus_dma_tag_create(sc->parent_tag,         /* parent */
568130391Sle                           TWS_ALIGNMENT,          /* alignment */
569130391Sle                           0,                      /* boundary */
570130391Sle                           BUS_SPACE_MAXADDR_32BIT,/* lowaddr */
571130391Sle                           BUS_SPACE_MAXADDR,      /* highaddr */
572130391Sle                           NULL, NULL,             /* filter, filterarg */
573130391Sle                           TWS_MAX_IO_SIZE,        /* maxsize */
574130391Sle                           max_sg_elements,        /* nsegments */
575130391Sle                           TWS_MAX_IO_SIZE,        /* maxsegsize */
576130391Sle                           BUS_DMA_ALLOCNOW,       /* flags */
577130391Sle                           busdma_lock_mutex,      /* lockfunc */
578130391Sle                           &sc->io_lock,           /* lockfuncarg */
579130391Sle                           &sc->data_tag           /* tag */)) {
580130391Sle        TWS_TRACE_DEBUG(sc, "DMA cmd tag Create fail", max_sg_elements, sc->is64bit);
581130391Sle        return(ENOMEM);
582130391Sle    }
583130391Sle
584130391Sle    sc->reqs = malloc(sizeof(struct tws_request) * tws_queue_depth, M_TWS,
585130391Sle                      M_WAITOK | M_ZERO);
586130391Sle    if ( sc->reqs == NULL ) {
587130391Sle        TWS_TRACE_DEBUG(sc, "malloc failed", 0, sc->is64bit);
588130391Sle        return(ENOMEM);
589130391Sle    }
590130391Sle    sc->sense_bufs = malloc(sizeof(struct tws_sense) * tws_queue_depth, M_TWS,
591130391Sle                      M_WAITOK | M_ZERO);
592130391Sle    if ( sc->sense_bufs == NULL ) {
593130391Sle        TWS_TRACE_DEBUG(sc, "sense malloc failed", 0, sc->is64bit);
594130391Sle        return(ENOMEM);
595130391Sle    }
596130391Sle    sc->scan_ccb = malloc(sizeof(union ccb), M_TWS, M_WAITOK | M_ZERO);
597130391Sle    if ( sc->scan_ccb == NULL ) {
598130391Sle        TWS_TRACE_DEBUG(sc, "ccb malloc failed", 0, sc->is64bit);
599130391Sle        return(ENOMEM);
600130391Sle    }
601130391Sle    if (bus_dmamem_alloc(sc->data_tag, (void **)&sc->ioctl_data_mem,
602130391Sle            (BUS_DMA_NOWAIT | BUS_DMA_ZERO), &sc->ioctl_data_map)) {
603130391Sle        device_printf(sc->tws_dev, "Cannot allocate ioctl data mem\n");
604130391Sle        return(ENOMEM);
605130391Sle    }
606130391Sle
607130391Sle    if ( !tws_ctlr_ready(sc) )
608130391Sle        if( !tws_ctlr_reset(sc) )
609130391Sle            return(FAILURE);
610130391Sle
611130391Sle    bzero(&sc->stats, sizeof(struct tws_stats));
612138110Sle    tws_init_qs(sc);
613138110Sle    tws_turn_off_interrupts(sc);
614138110Sle
615138110Sle    /*
616138110Sle     * enable pull mode by setting bit1 .
617138110Sle     * setting bit0 to 1 will enable interrupt coalesing
618138110Sle     * will revisit.
619138110Sle     */
620138110Sle
621138110Sle#ifdef TWS_PULL_MODE_ENABLE
622138110Sle
623138110Sle    reg = tws_read_reg(sc, TWS_I2O0_CTL, 4);
624138110Sle    TWS_TRACE_DEBUG(sc, "i20 ctl", reg, TWS_I2O0_CTL);
625138110Sle    tws_write_reg(sc, TWS_I2O0_CTL, reg | TWS_BIT1, 4);
626138110Sle
627138110Sle#endif
628138110Sle
629138110Sle    TWS_TRACE_DEBUG(sc, "dma_mem_phys", sc->dma_mem_phys, TWS_I2O0_CTL);
630138110Sle    if ( tws_init_reqs(sc, dma_mem_size) == FAILURE )
631138110Sle        return(FAILURE);
632138110Sle    if ( tws_init_aen_q(sc) == FAILURE )
633138110Sle        return(FAILURE);
634138110Sle
635138110Sle    return(SUCCESS);
636138110Sle
637138110Sle}
638138110Sle
639138110Slestatic int
640138110Sletws_init_aen_q(struct tws_softc *sc)
641138110Sle{
642138110Sle    sc->aen_q.head=0;
643138110Sle    sc->aen_q.tail=0;
644138110Sle    sc->aen_q.depth=256;
645138110Sle    sc->aen_q.overflow=0;
646138110Sle    sc->aen_q.q = malloc(sizeof(struct tws_event_packet)*sc->aen_q.depth,
647138110Sle                              M_TWS, M_WAITOK | M_ZERO);
648138110Sle    if ( ! sc->aen_q.q )
649138110Sle        return(FAILURE);
650138110Sle    return(SUCCESS);
651138110Sle}
652138110Sle
653138110Slestatic int
654138110Sletws_init_trace_q(struct tws_softc *sc)
655138110Sle{
656138110Sle    sc->trace_q.head=0;
657138110Sle    sc->trace_q.tail=0;
658138110Sle    sc->trace_q.depth=256;
659138110Sle    sc->trace_q.overflow=0;
660138110Sle    sc->trace_q.q = malloc(sizeof(struct tws_trace_rec)*sc->trace_q.depth,
661138110Sle                              M_TWS, M_WAITOK | M_ZERO);
662138110Sle    if ( ! sc->trace_q.q )
663138110Sle        return(FAILURE);
664138110Sle    return(SUCCESS);
665138110Sle}
666138110Sle
667138110Slestatic int
668138110Sletws_init_reqs(struct tws_softc *sc, u_int32_t dma_mem_size)
669138110Sle{
670138110Sle
671138110Sle    struct tws_command_packet *cmd_buf;
672138110Sle    cmd_buf = (struct tws_command_packet *)sc->dma_mem;
673138110Sle    int i;
674138110Sle
675138110Sle    bzero(cmd_buf, dma_mem_size);
676138110Sle    TWS_TRACE_DEBUG(sc, "phy cmd", sc->dma_mem_phys, 0);
677138110Sle    mtx_lock(&sc->q_lock);
678138110Sle    for ( i=0; i< tws_queue_depth; i++)
679138110Sle    {
680138110Sle        if (bus_dmamap_create(sc->data_tag, 0, &sc->reqs[i].dma_map)) {
681138110Sle            /* log a ENOMEM failure msg here */
682138110Sle            mtx_unlock(&sc->q_lock);
683138110Sle            return(FAILURE);
684138110Sle        }
685138110Sle        sc->reqs[i].cmd_pkt =  &cmd_buf[i];
686138110Sle
687138110Sle        sc->sense_bufs[i].hdr = &cmd_buf[i].hdr ;
688138110Sle        sc->sense_bufs[i].hdr_pkt_phy = sc->dma_mem_phys +
689138110Sle                              (i * sizeof(struct tws_command_packet));
690138110Sle
691138110Sle        sc->reqs[i].cmd_pkt_phy = sc->dma_mem_phys +
692138110Sle                              sizeof(struct tws_command_header) +
693138110Sle                              (i * sizeof(struct tws_command_packet));
694138110Sle        sc->reqs[i].request_id = i;
695130391Sle        sc->reqs[i].sc = sc;
696130391Sle
697130391Sle        sc->reqs[i].cmd_pkt->hdr.header_desc.size_header = 128;
698130391Sle
699130391Sle        sc->reqs[i].state = TWS_REQ_STATE_FREE;
700130391Sle        if ( i >= TWS_RESERVED_REQS )
701130391Sle            tws_q_insert_tail(sc, &sc->reqs[i], TWS_FREE_Q);
702130391Sle    }
703130391Sle    mtx_unlock(&sc->q_lock);
704130391Sle    return(SUCCESS);
705130391Sle}
706130391Sle
707130391Slestatic void
708130391Sletws_dmamap_cmds_load_cbfn(void *arg, bus_dma_segment_t *segs,
709130391Sle                           int nseg, int error)
710130391Sle{
711130391Sle
712130391Sle    /* printf("command load done \n"); */
713130391Sle
714130391Sle    *((bus_addr_t *)arg) = segs[0].ds_addr;
715130391Sle}
716130391Sle
717130391Slevoid
718130391Sletws_send_event(struct tws_softc *sc, u_int8_t event)
719130391Sle{
720130391Sle    mtx_assert(&sc->gen_lock, MA_OWNED);
721130391Sle    TWS_TRACE_DEBUG(sc, "received event ", 0, event);
722130391Sle    switch (event) {
723130391Sle
724130391Sle        case TWS_INIT_START:
725130391Sle            sc->tws_state = TWS_INIT;
726130391Sle            break;
727130391Sle
728130391Sle        case TWS_INIT_COMPLETE:
729130391Sle            if (sc->tws_state != TWS_INIT) {
730130391Sle                device_printf(sc->tws_dev, "invalid state transition %d => TWS_ONLINE\n", sc->tws_state);
731130391Sle            } else {
732130391Sle                sc->tws_state = TWS_ONLINE;
733130391Sle            }
734130391Sle            break;
735130391Sle
736130391Sle        case TWS_RESET_START:
737130391Sle            /* We can transition to reset state from any state except reset*/
738130391Sle            if (sc->tws_state != TWS_RESET) {
739130391Sle                sc->tws_prev_state = sc->tws_state;
740130391Sle                sc->tws_state = TWS_RESET;
741130391Sle            }
742130391Sle            break;
743130391Sle
744130391Sle        case TWS_RESET_COMPLETE:
745130391Sle            if (sc->tws_state != TWS_RESET) {
746130391Sle                device_printf(sc->tws_dev, "invalid state transition %d => %d (previous state)\n", sc->tws_state, sc->tws_prev_state);
747130391Sle            } else {
748130391Sle                sc->tws_state = sc->tws_prev_state;
749130391Sle            }
750130391Sle            break;
751130391Sle
752130391Sle        case TWS_SCAN_FAILURE:
753130391Sle            if (sc->tws_state != TWS_ONLINE) {
754130391Sle                device_printf(sc->tws_dev, "invalid state transition %d => TWS_OFFLINE\n", sc->tws_state);
755130391Sle            } else {
756130391Sle                sc->tws_state = TWS_OFFLINE;
757130391Sle            }
758130391Sle            break;
759130391Sle
760130391Sle        case TWS_UNINIT_START:
761130391Sle            if ((sc->tws_state != TWS_ONLINE) && (sc->tws_state != TWS_OFFLINE)) {
762130391Sle                device_printf(sc->tws_dev, "invalid state transition %d => TWS_UNINIT\n", sc->tws_state);
763130391Sle            } else {
764130391Sle                sc->tws_state = TWS_UNINIT;
765130391Sle            }
766130391Sle            break;
767130391Sle    }
768130391Sle
769130391Sle}
770130391Sle
771130391Sleuint8_t
772130391Sletws_get_state(struct tws_softc *sc)
773130391Sle{
774130391Sle
775130391Sle    return((u_int8_t)sc->tws_state);
776130391Sle
777130391Sle}
778130391Sle
779130391Sle/* Called during system shutdown after sync. */
780130391Sle
781130391Slestatic int
782130391Sletws_shutdown(device_t dev)
783130391Sle{
784130391Sle
785130391Sle    struct tws_softc *sc = device_get_softc(dev);
786130391Sle
787130391Sle    TWS_TRACE_DEBUG(sc, "entry", 0, 0);
788130391Sle
789130391Sle    tws_turn_off_interrupts(sc);
790130391Sle    tws_init_connect(sc, 1);
791130391Sle
792130391Sle    return (0);
793130391Sle}
794130391Sle
795130391Sle/*
796130391Sle * Device suspend routine.
797130391Sle */
798130391Slestatic int
799130391Sletws_suspend(device_t dev)
800130391Sle{
801130391Sle    struct tws_softc *sc = device_get_softc(dev);
802130391Sle
803130391Sle    if ( sc )
804130391Sle        TWS_TRACE_DEBUG(sc, "entry", 0, 0);
805130391Sle    return (0);
806130391Sle}
807130391Sle
808130391Sle/*
809130391Sle * Device resume routine.
810130391Sle */
811130391Slestatic int
812130391Sletws_resume(device_t dev)
813130391Sle{
814130391Sle
815130391Sle    struct tws_softc *sc = device_get_softc(dev);
816130391Sle
817130391Sle    if ( sc )
818130391Sle        TWS_TRACE_DEBUG(sc, "entry", 0, 0);
819130391Sle    return (0);
820130391Sle}
821130391Sle
822130391Sle
823130391Slestruct tws_request *
824130391Sletws_get_request(struct tws_softc *sc, u_int16_t type)
825130391Sle{
826130391Sle    struct mtx *my_mutex = ((type == TWS_REQ_TYPE_SCSI_IO) ? &sc->q_lock : &sc->gen_lock);
827130391Sle    struct tws_request *r = NULL;
828130391Sle
829130391Sle    mtx_lock(my_mutex);
830130391Sle
831130391Sle    if (type == TWS_REQ_TYPE_SCSI_IO) {
832130391Sle        r = tws_q_remove_head(sc, TWS_FREE_Q);
833130391Sle    } else {
834130391Sle        if ( sc->reqs[type].state == TWS_REQ_STATE_FREE ) {
835130391Sle            r = &sc->reqs[type];
836130391Sle        }
837130391Sle    }
838130391Sle
839130391Sle    if ( r ) {
840130391Sle        bzero(&r->cmd_pkt->cmd, sizeof(struct tws_command_apache));
841130391Sle        r->data = NULL;
842130391Sle        r->length = 0;
843130391Sle        r->type = type;
844130391Sle        r->flags = TWS_DIR_UNKNOWN;
845130391Sle        r->error_code = TWS_REQ_RET_INVALID;
846130391Sle        r->cb = NULL;
847130391Sle        r->ccb_ptr = NULL;
848130391Sle        r->thandle.callout = NULL;
849130391Sle        r->next = r->prev = NULL;
850130391Sle
851130391Sle        r->state = ((type == TWS_REQ_TYPE_SCSI_IO) ? TWS_REQ_STATE_TRAN : TWS_REQ_STATE_BUSY);
852130391Sle    }
853130391Sle
854130391Sle    mtx_unlock(my_mutex);
855130391Sle
856130391Sle    return(r);
857130391Sle}
858130391Sle
859130391Slevoid
860138112Sletws_release_request(struct tws_request *req)
861138112Sle{
862130391Sle
863130391Sle    struct tws_softc *sc = req->sc;
864130391Sle
865130391Sle    TWS_TRACE_DEBUG(sc, "entry", sc, 0);
866138110Sle    mtx_lock(&sc->q_lock);
867138110Sle    tws_q_insert_tail(sc, req, TWS_FREE_Q);
868138110Sle    mtx_unlock(&sc->q_lock);
869138110Sle}
870130391Sle
871130391Slestatic device_method_t tws_methods[] = {
872130391Sle    /* Device interface */
873130391Sle    DEVMETHOD(device_probe,     tws_probe),
874130391Sle    DEVMETHOD(device_attach,    tws_attach),
875130391Sle    DEVMETHOD(device_detach,    tws_detach),
876130391Sle    DEVMETHOD(device_shutdown,  tws_shutdown),
877130391Sle    DEVMETHOD(device_suspend,   tws_suspend),
878130391Sle    DEVMETHOD(device_resume,    tws_resume),
879130391Sle
880130391Sle    DEVMETHOD_END
881130391Sle};
882130391Sle
883130391Slestatic driver_t tws_driver = {
884130391Sle        "tws",
885130391Sle        tws_methods,
886130391Sle        sizeof(struct tws_softc)
887130391Sle};
888130391Sle
889130391Sle
890130391Slestatic devclass_t tws_devclass;
891130391Sle
892130391Sle/* DEFINE_CLASS_0(tws, tws_driver, tws_methods, sizeof(struct tws_softc)); */
893130391SleDRIVER_MODULE(tws, pci, tws_driver, tws_devclass, 0, 0);
894130391SleMODULE_DEPEND(tws, cam, 1, 1, 1);
895130391SleMODULE_DEPEND(tws, pci, 1, 1, 1);
896130391Sle
897130391SleTUNABLE_INT("hw.tws.queue_depth", &tws_queue_depth);
898130391SleTUNABLE_INT("hw.tws.enable_msi", &tws_enable_msi);
899130391Sle