197a077a0SMatthew Dillon /*
297a077a0SMatthew Dillon * Copyright (c) 2016 The DragonFly Project. All rights reserved.
397a077a0SMatthew Dillon *
497a077a0SMatthew Dillon * This code is derived from software contributed to The DragonFly Project
597a077a0SMatthew Dillon * by Matthew Dillon <dillon@backplane.com>
697a077a0SMatthew Dillon *
797a077a0SMatthew Dillon * Redistribution and use in source and binary forms, with or without
897a077a0SMatthew Dillon * modification, are permitted provided that the following conditions
997a077a0SMatthew Dillon * are met:
1097a077a0SMatthew Dillon *
1197a077a0SMatthew Dillon * 1. Redistributions of source code must retain the above copyright
1297a077a0SMatthew Dillon * notice, this list of conditions and the following disclaimer.
1397a077a0SMatthew Dillon * 2. Redistributions in binary form must reproduce the above copyright
1497a077a0SMatthew Dillon * notice, this list of conditions and the following disclaimer in
1597a077a0SMatthew Dillon * the documentation and/or other materials provided with the
1697a077a0SMatthew Dillon * distribution.
1797a077a0SMatthew Dillon * 3. Neither the name of The DragonFly Project nor the names of its
1897a077a0SMatthew Dillon * contributors may be used to endorse or promote products derived
1997a077a0SMatthew Dillon * from this software without specific, prior written permission.
2097a077a0SMatthew Dillon *
2197a077a0SMatthew Dillon * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
2297a077a0SMatthew Dillon * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
2397a077a0SMatthew Dillon * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
2497a077a0SMatthew Dillon * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
2597a077a0SMatthew Dillon * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
2697a077a0SMatthew Dillon * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
2797a077a0SMatthew Dillon * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
2897a077a0SMatthew Dillon * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
2997a077a0SMatthew Dillon * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
3097a077a0SMatthew Dillon * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
3197a077a0SMatthew Dillon * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3297a077a0SMatthew Dillon * SUCH DAMAGE.
3397a077a0SMatthew Dillon */
3497a077a0SMatthew Dillon /*
3597a077a0SMatthew Dillon * Primary device interface for NVME driver, for DragonFlyBSD
3697a077a0SMatthew Dillon */
3797a077a0SMatthew Dillon
3897a077a0SMatthew Dillon #include "nvme.h"
3997a077a0SMatthew Dillon
4097a077a0SMatthew Dillon /*
4197a077a0SMatthew Dillon * Device bus methods
4297a077a0SMatthew Dillon */
4397a077a0SMatthew Dillon static int nvme_probe (device_t dev);
4497a077a0SMatthew Dillon static int nvme_attach (device_t dev);
4597a077a0SMatthew Dillon static int nvme_detach (device_t dev);
4697a077a0SMatthew Dillon static int nvme_shutdown (device_t dev);
47*11759406SMatthew Dillon #if 0
4897a077a0SMatthew Dillon static int nvme_suspend (device_t dev);
4997a077a0SMatthew Dillon static int nvme_resume (device_t dev);
5097a077a0SMatthew Dillon #endif
5197a077a0SMatthew Dillon
5297a077a0SMatthew Dillon static device_method_t nvme_methods[] = {
5397a077a0SMatthew Dillon DEVMETHOD(device_probe, nvme_probe),
5497a077a0SMatthew Dillon DEVMETHOD(device_attach, nvme_attach),
5597a077a0SMatthew Dillon DEVMETHOD(device_detach, nvme_detach),
5697a077a0SMatthew Dillon DEVMETHOD(device_shutdown, nvme_shutdown),
57*11759406SMatthew Dillon #if 0
5897a077a0SMatthew Dillon DEVMETHOD(device_suspend, nvme_suspend),
5997a077a0SMatthew Dillon DEVMETHOD(device_resume, nvme_resume),
6097a077a0SMatthew Dillon #endif
6197a077a0SMatthew Dillon
6297a077a0SMatthew Dillon DEVMETHOD(bus_print_child, bus_generic_print_child),
6397a077a0SMatthew Dillon DEVMETHOD(bus_driver_added, bus_generic_driver_added),
6497a077a0SMatthew Dillon DEVMETHOD_END
6597a077a0SMatthew Dillon };
6697a077a0SMatthew Dillon
6797a077a0SMatthew Dillon static devclass_t nvme_devclass;
6897a077a0SMatthew Dillon
6997a077a0SMatthew Dillon static driver_t nvme_driver = {
7097a077a0SMatthew Dillon "nvme",
7197a077a0SMatthew Dillon nvme_methods,
7297a077a0SMatthew Dillon sizeof(nvme_softc_t)
7397a077a0SMatthew Dillon };
7497a077a0SMatthew Dillon
7597a077a0SMatthew Dillon DRIVER_MODULE(nvme, pci, nvme_driver, nvme_devclass, NULL, NULL);
7697a077a0SMatthew Dillon MODULE_VERSION(nvme, 1);
7797a077a0SMatthew Dillon
7897a077a0SMatthew Dillon /*
7997a077a0SMatthew Dillon * Device bus method procedures
8097a077a0SMatthew Dillon */
8197a077a0SMatthew Dillon static int
nvme_probe(device_t dev)8297a077a0SMatthew Dillon nvme_probe(device_t dev)
8397a077a0SMatthew Dillon {
8497a077a0SMatthew Dillon const nvme_device_t *ad;
8597a077a0SMatthew Dillon
8697a077a0SMatthew Dillon if (kgetenv("hint.nvme.disabled"))
8797a077a0SMatthew Dillon return(ENXIO);
8897a077a0SMatthew Dillon
8997a077a0SMatthew Dillon ad = nvme_lookup_device(dev);
9097a077a0SMatthew Dillon if (ad) {
9197a077a0SMatthew Dillon device_set_desc(dev, ad->name);
9297a077a0SMatthew Dillon return(-5); /* higher priority the NATA */
9397a077a0SMatthew Dillon }
9497a077a0SMatthew Dillon return(ENXIO);
9597a077a0SMatthew Dillon }
9697a077a0SMatthew Dillon
9797a077a0SMatthew Dillon static int
nvme_attach(device_t dev)9897a077a0SMatthew Dillon nvme_attach(device_t dev)
9997a077a0SMatthew Dillon {
10097a077a0SMatthew Dillon nvme_softc_t *sc = device_get_softc(dev);
10197a077a0SMatthew Dillon int error;
10297a077a0SMatthew Dillon
10397a077a0SMatthew Dillon sc->dev = dev;
10497a077a0SMatthew Dillon sc->ad = nvme_lookup_device(dev);
10597a077a0SMatthew Dillon if (sc->ad == NULL)
10697a077a0SMatthew Dillon return(ENXIO);
10797a077a0SMatthew Dillon
10897a077a0SMatthew Dillon /* sanity check critical structure sizes */
10997a077a0SMatthew Dillon KKASSERT(sizeof(nvme_admin_data_t) == NVME_MAX_ADMIN_BUFFER);
11097a077a0SMatthew Dillon KKASSERT(sizeof(nvme_allres_t) == 16);
11197a077a0SMatthew Dillon KKASSERT(sizeof(nvme_allcmd_t) == 64);
11297a077a0SMatthew Dillon
11397a077a0SMatthew Dillon error = sc->ad->attach(dev);
11497a077a0SMatthew Dillon
11597a077a0SMatthew Dillon return error;
11697a077a0SMatthew Dillon }
11797a077a0SMatthew Dillon
11897a077a0SMatthew Dillon static int
nvme_detach(device_t dev)11997a077a0SMatthew Dillon nvme_detach(device_t dev)
12097a077a0SMatthew Dillon {
12197a077a0SMatthew Dillon nvme_softc_t *sc = device_get_softc(dev);
12297a077a0SMatthew Dillon int error = 0;
12397a077a0SMatthew Dillon
12497a077a0SMatthew Dillon if (sc->ad) {
12597a077a0SMatthew Dillon error = sc->ad->detach(dev);
12697a077a0SMatthew Dillon sc->ad = NULL;
12797a077a0SMatthew Dillon }
12897a077a0SMatthew Dillon return(error);
12997a077a0SMatthew Dillon }
13097a077a0SMatthew Dillon
131*11759406SMatthew Dillon /*
132*11759406SMatthew Dillon * System halt/reboot
133*11759406SMatthew Dillon */
13497a077a0SMatthew Dillon static int
nvme_shutdown(device_t dev)13597a077a0SMatthew Dillon nvme_shutdown(device_t dev)
13697a077a0SMatthew Dillon {
137*11759406SMatthew Dillon return nvme_detach(dev);
13897a077a0SMatthew Dillon }
13997a077a0SMatthew Dillon
140*11759406SMatthew Dillon #if 0
141*11759406SMatthew Dillon
14297a077a0SMatthew Dillon static int
14397a077a0SMatthew Dillon nvme_suspend(device_t dev)
14497a077a0SMatthew Dillon {
14597a077a0SMatthew Dillon return (0);
14697a077a0SMatthew Dillon }
14797a077a0SMatthew Dillon
14897a077a0SMatthew Dillon static int
14997a077a0SMatthew Dillon nvme_resume(device_t dev)
15097a077a0SMatthew Dillon {
15197a077a0SMatthew Dillon return (0);
15297a077a0SMatthew Dillon }
15397a077a0SMatthew Dillon
15497a077a0SMatthew Dillon #endif
15597a077a0SMatthew Dillon
15697a077a0SMatthew Dillon /*
15797a077a0SMatthew Dillon * Chipset register accesses (NVME_REG_*)
15897a077a0SMatthew Dillon */
15997a077a0SMatthew Dillon u_int32_t
nvme_read(nvme_softc_t * sc,bus_size_t r)16097a077a0SMatthew Dillon nvme_read(nvme_softc_t *sc, bus_size_t r)
16197a077a0SMatthew Dillon {
16297a077a0SMatthew Dillon bus_space_barrier(sc->iot, sc->ioh, r, 4,
16397a077a0SMatthew Dillon BUS_SPACE_BARRIER_READ);
16497a077a0SMatthew Dillon return (bus_space_read_4(sc->iot, sc->ioh, r));
16597a077a0SMatthew Dillon }
16697a077a0SMatthew Dillon
16797a077a0SMatthew Dillon u_int64_t
nvme_read8(nvme_softc_t * sc,bus_size_t r)16897a077a0SMatthew Dillon nvme_read8(nvme_softc_t *sc, bus_size_t r)
16997a077a0SMatthew Dillon {
17097a077a0SMatthew Dillon bus_space_barrier(sc->iot, sc->ioh, r, 8,
17197a077a0SMatthew Dillon BUS_SPACE_BARRIER_READ);
17297a077a0SMatthew Dillon return (bus_space_read_8(sc->iot, sc->ioh, r));
17397a077a0SMatthew Dillon }
17497a077a0SMatthew Dillon
17597a077a0SMatthew Dillon void
nvme_write(nvme_softc_t * sc,bus_size_t r,u_int32_t v)17697a077a0SMatthew Dillon nvme_write(nvme_softc_t *sc, bus_size_t r, u_int32_t v)
17797a077a0SMatthew Dillon {
17897a077a0SMatthew Dillon bus_space_write_4(sc->iot, sc->ioh, r, v);
17997a077a0SMatthew Dillon bus_space_barrier(sc->iot, sc->ioh, r, 4,
18097a077a0SMatthew Dillon BUS_SPACE_BARRIER_WRITE);
18197a077a0SMatthew Dillon }
18297a077a0SMatthew Dillon
18397a077a0SMatthew Dillon void
nvme_write8(nvme_softc_t * sc,bus_size_t r,u_int64_t v)18497a077a0SMatthew Dillon nvme_write8(nvme_softc_t *sc, bus_size_t r, u_int64_t v)
18597a077a0SMatthew Dillon {
18697a077a0SMatthew Dillon bus_space_write_8(sc->iot, sc->ioh, r, v);
18797a077a0SMatthew Dillon bus_space_barrier(sc->iot, sc->ioh, r, 8,
18897a077a0SMatthew Dillon BUS_SPACE_BARRIER_WRITE);
18997a077a0SMatthew Dillon }
19097a077a0SMatthew Dillon
19197a077a0SMatthew Dillon /*
19297a077a0SMatthew Dillon * Sleep (ms) milliseconds, error on the side of caution.
19397a077a0SMatthew Dillon */
19497a077a0SMatthew Dillon void
nvme_os_sleep(int ms)19597a077a0SMatthew Dillon nvme_os_sleep(int ms)
19697a077a0SMatthew Dillon {
19797a077a0SMatthew Dillon int slpticks;
19897a077a0SMatthew Dillon
19997a077a0SMatthew Dillon slpticks = hz * ms / 1000 + 1;
20097a077a0SMatthew Dillon tsleep(&slpticks, 0, "nvslp", slpticks);
20197a077a0SMatthew Dillon }
20297a077a0SMatthew Dillon
20397a077a0SMatthew Dillon /*
20497a077a0SMatthew Dillon * Sleep for a minimum interval and return the number of milliseconds
20597a077a0SMatthew Dillon * that was. The minimum value returned is 1
20697a077a0SMatthew Dillon */
20797a077a0SMatthew Dillon int
nvme_os_softsleep(void)20897a077a0SMatthew Dillon nvme_os_softsleep(void)
20997a077a0SMatthew Dillon {
21097a077a0SMatthew Dillon int slpticks = 0;
21197a077a0SMatthew Dillon
21297a077a0SMatthew Dillon if (hz >= 1000) {
21397a077a0SMatthew Dillon tsleep(&slpticks, 0, "nvslp", hz / 1000);
21497a077a0SMatthew Dillon return(1);
21597a077a0SMatthew Dillon } else {
21697a077a0SMatthew Dillon tsleep(&slpticks, 0, "nvslp", 1);
21797a077a0SMatthew Dillon return(1000 / hz);
21897a077a0SMatthew Dillon }
21997a077a0SMatthew Dillon }
22097a077a0SMatthew Dillon
22197a077a0SMatthew Dillon void
nvme_os_hardsleep(int us)22297a077a0SMatthew Dillon nvme_os_hardsleep(int us)
22397a077a0SMatthew Dillon {
22497a077a0SMatthew Dillon DELAY(us);
22597a077a0SMatthew Dillon }
226