1/*-
2 * Copyright (C) 2017 Olivier Houchard
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 *    notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 *    notice, this list of conditions and the following disclaimer in the
11 *    documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
14 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
16 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
17 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
18 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
19 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
20 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
21 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
22 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
23 * SUCH DAMAGE.
24 */
25
26#include <sys/param.h>
27#include <sys/systm.h>
28#include <sys/buf.h>
29#include <sys/bus.h>
30#include <sys/conf.h>
31#include <sys/proc.h>
32#include <sys/smp.h>
33
34#include "nvme_private.h"
35
36static int    nvme_ahci_probe(device_t dev);
37static int    nvme_ahci_attach(device_t dev);
38static int    nvme_ahci_detach(device_t dev);
39
40static device_method_t nvme_ahci_methods[] = {
41	/* Device interface */
42	DEVMETHOD(device_probe,     nvme_ahci_probe),
43	DEVMETHOD(device_attach,    nvme_ahci_attach),
44	DEVMETHOD(device_detach,    nvme_ahci_detach),
45	DEVMETHOD(device_shutdown,  nvme_shutdown),
46	{ 0, 0 }
47};
48
49static driver_t nvme_ahci_driver = {
50	"nvme",
51	nvme_ahci_methods,
52	sizeof(struct nvme_controller),
53};
54
55DRIVER_MODULE(nvme, ahci, nvme_ahci_driver, NULL, NULL);
56
57static int
58nvme_ahci_probe (device_t device)
59{
60	return (0);
61}
62
63static int
64nvme_ahci_attach(device_t dev)
65{
66	struct nvme_controller*ctrlr = DEVICE2SOFTC(dev);
67	int ret;
68
69	/* Map MMIO registers */
70	ctrlr->resource_id = 0;
71
72	ctrlr->resource = bus_alloc_resource_any(dev, SYS_RES_MEMORY,
73	    &ctrlr->resource_id, RF_ACTIVE);
74
75	if(ctrlr->resource == NULL) {
76		nvme_printf(ctrlr, "unable to allocate mem resource\n");
77		ret = ENOMEM;
78		goto bad;
79	}
80	ctrlr->bus_tag = rman_get_bustag(ctrlr->resource);
81	ctrlr->bus_handle = rman_get_bushandle(ctrlr->resource);
82	ctrlr->regs = (struct nvme_registers *)ctrlr->bus_handle;
83
84	/* Allocate and setup IRQ */
85	ctrlr->rid = 0;
86	ctrlr->res = bus_alloc_resource_any(dev, SYS_RES_IRQ,
87	    &ctrlr->rid, RF_SHAREABLE | RF_ACTIVE);
88	if (ctrlr->res == NULL) {
89		nvme_printf(ctrlr, "unable to allocate shared interrupt\n");
90		ret = ENOMEM;
91		goto bad;
92	}
93
94	ctrlr->msi_count = 0;
95	ctrlr->num_io_queues = 1;
96	if (bus_setup_intr(dev, ctrlr->res,
97	    INTR_TYPE_MISC | INTR_MPSAFE, NULL, nvme_ctrlr_shared_handler,
98	    ctrlr, &ctrlr->tag) != 0) {
99		nvme_printf(ctrlr, "unable to setup shared interrupt\n");
100		ret = ENOMEM;
101		goto bad;
102	}
103	ctrlr->tag = (void *)0x1;
104
105	/*
106	 * We're attached via this funky mechanism. Flag the controller so that
107	 * it avoids things that can't work when we do that, like asking for
108	 * PCI config space entries.
109	 */
110	ctrlr->quirks |= QUIRK_AHCI;
111
112	return (nvme_attach(dev));	/* Note: failure frees resources */
113bad:
114	if (ctrlr->resource != NULL) {
115		bus_release_resource(dev, SYS_RES_MEMORY,
116		    ctrlr->resource_id, ctrlr->resource);
117	}
118	if (ctrlr->res)
119		bus_release_resource(ctrlr->dev, SYS_RES_IRQ,
120		    rman_get_rid(ctrlr->res), ctrlr->res);
121	return (ret);
122}
123
124static int
125nvme_ahci_detach(device_t dev)
126{
127
128	return (nvme_detach(dev));
129}
130