Spdk/test/external_code/nvme/nvme.c
Konrad Sztyber ad9ccbbf52 external_code/nvme: controller register access functions
Added getters/setters providing access to several of the NVMe
controller's registers.  Only the registers that are needed for the
initialization are implemented.

For now all of them are unused, so they're marked as external to avoid
the -Wunused-function warnings.  The subsequent patches will make use of
them and mark as static appropriately.

Signed-off-by: Konrad Sztyber <konrad.sztyber@intel.com>
Change-Id: I7012583f74e87720f6915afca69474ad1bb1e377
Reviewed-on: https://review.spdk.io/gerrit/c/spdk/spdk/+/6668
Tested-by: SPDK CI Jenkins <sys_sgci@intel.com>
Community-CI: Mellanox Build Bot
Reviewed-by: Tomasz Zawadzki <tomasz.zawadzki@intel.com>
Reviewed-by: Jim Harris <james.r.harris@intel.com>
Reviewed-by: Ben Walker <benjamin.walker@intel.com>
2021-03-09 08:28:22 +00:00

280 lines
7.8 KiB
C

/*-
* BSD LICENSE
*
* Copyright (c) Intel Corporation.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Intel Corporation nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "spdk/mmio.h"
#include "spdk/nvme_spec.h"
#include "spdk/log.h"
#include "spdk/stdinc.h"
#include "nvme.h"
struct nvme_ctrlr {
/* Underlying PCI device */
struct spdk_pci_device *pci_device;
/* Pointer to the MMIO register space */
volatile struct spdk_nvme_registers *regs;
TAILQ_ENTRY(nvme_ctrlr) tailq;
};
static struct spdk_pci_id nvme_pci_driver_id[] = {
{
.class_id = SPDK_PCI_CLASS_NVME,
.vendor_id = SPDK_PCI_ANY_ID,
.device_id = SPDK_PCI_ANY_ID,
.subvendor_id = SPDK_PCI_ANY_ID,
.subdevice_id = SPDK_PCI_ANY_ID,
},
{ .vendor_id = 0, /* sentinel */ },
};
SPDK_PCI_DRIVER_REGISTER(nvme_external, nvme_pci_driver_id, SPDK_PCI_DRIVER_NEED_MAPPING);
static TAILQ_HEAD(, nvme_ctrlr) g_nvme_ctrlrs = TAILQ_HEAD_INITIALIZER(g_nvme_ctrlrs);
static struct nvme_ctrlr *
find_ctrlr_by_addr(struct spdk_pci_addr *addr)
{
struct spdk_pci_addr ctrlr_addr;
struct nvme_ctrlr *ctrlr;
TAILQ_FOREACH(ctrlr, &g_nvme_ctrlrs, tailq) {
ctrlr_addr = spdk_pci_device_get_addr(ctrlr->pci_device);
if (spdk_pci_addr_compare(addr, &ctrlr_addr) == 0) {
return ctrlr;
}
}
return NULL;
}
static volatile void *
get_pcie_reg_addr(struct nvme_ctrlr *ctrlr, uint32_t offset)
{
return (volatile void *)((uintptr_t)ctrlr->regs + offset);
}
static void
get_pcie_reg_4(struct nvme_ctrlr *ctrlr, uint32_t offset, uint32_t *value)
{
assert(offset <= sizeof(struct spdk_nvme_registers) - 4);
*value = spdk_mmio_read_4(get_pcie_reg_addr(ctrlr, offset));
}
static void
get_pcie_reg_8(struct nvme_ctrlr *ctrlr, uint32_t offset, uint64_t *value)
{
assert(offset <= sizeof(struct spdk_nvme_registers) - 8);
*value = spdk_mmio_read_8(get_pcie_reg_addr(ctrlr, offset));
}
static void
set_pcie_reg_4(struct nvme_ctrlr *ctrlr, uint32_t offset, uint32_t value)
{
assert(offset <= sizeof(struct spdk_nvme_registers) - 4);
spdk_mmio_write_4(get_pcie_reg_addr(ctrlr, offset), value);
}
static void
set_pcie_reg_8(struct nvme_ctrlr *ctrlr, uint32_t offset, uint64_t value)
{
assert(offset <= sizeof(struct spdk_nvme_registers) - 8);
spdk_mmio_write_8(get_pcie_reg_addr(ctrlr, offset), value);
}
void nvme_ctrlr_get_cap(struct nvme_ctrlr *ctrlr, union spdk_nvme_cap_register *cap);
void
nvme_ctrlr_get_cap(struct nvme_ctrlr *ctrlr, union spdk_nvme_cap_register *cap)
{
get_pcie_reg_8(ctrlr, offsetof(struct spdk_nvme_registers, cap), &cap->raw);
}
void
nvme_ctrlr_get_cc(struct nvme_ctrlr *ctrlr, union spdk_nvme_cc_register *cc);
void
nvme_ctrlr_get_cc(struct nvme_ctrlr *ctrlr, union spdk_nvme_cc_register *cc)
{
get_pcie_reg_4(ctrlr, offsetof(struct spdk_nvme_registers, cc), &cc->raw);
}
void nvme_ctrlr_get_csts(struct nvme_ctrlr *ctrlr, union spdk_nvme_csts_register *csts);
void
nvme_ctrlr_get_csts(struct nvme_ctrlr *ctrlr, union spdk_nvme_csts_register *csts)
{
get_pcie_reg_4(ctrlr, offsetof(struct spdk_nvme_registers, csts), &csts->raw);
}
void nvme_ctrlr_set_cc(struct nvme_ctrlr *ctrlr, const union spdk_nvme_cc_register *cc);
void
nvme_ctrlr_set_cc(struct nvme_ctrlr *ctrlr, const union spdk_nvme_cc_register *cc)
{
set_pcie_reg_4(ctrlr, offsetof(struct spdk_nvme_registers, cc.raw), cc->raw);
}
void nvme_ctrlr_set_asq(struct nvme_ctrlr *ctrlr, uint64_t value);
void
nvme_ctrlr_set_asq(struct nvme_ctrlr *ctrlr, uint64_t value)
{
set_pcie_reg_8(ctrlr, offsetof(struct spdk_nvme_registers, asq), value);
}
void nvme_ctrlr_set_acq(struct nvme_ctrlr *ctrlr, uint64_t value);
void
nvme_ctrlr_set_acq(struct nvme_ctrlr *ctrlr, uint64_t value)
{
set_pcie_reg_8(ctrlr, offsetof(struct spdk_nvme_registers, acq), value);
}
void nvme_ctrlr_set_aqa(struct nvme_ctrlr *ctrlr, const union spdk_nvme_aqa_register *aqa);
void
nvme_ctrlr_set_aqa(struct nvme_ctrlr *ctrlr, const union spdk_nvme_aqa_register *aqa)
{
set_pcie_reg_4(ctrlr, offsetof(struct spdk_nvme_registers, aqa.raw), aqa->raw);
}
static int
pcie_enum_cb(void *ctx, struct spdk_pci_device *pci_dev)
{
struct nvme_ctrlr *ctrlr;
TAILQ_HEAD(, nvme_ctrlr) *ctrlrs = ctx;
char addr[32] = {};
uint64_t phys_addr, size;
void *reg_addr;
spdk_pci_addr_fmt(addr, sizeof(addr), &pci_dev->addr);
ctrlr = calloc(1, sizeof(*ctrlr));
if (!ctrlr) {
SPDK_ERRLOG("Failed to allocate NVMe controller: %s\n", addr);
return -1;
}
if (spdk_pci_device_claim(pci_dev)) {
SPDK_ERRLOG("Failed to claim PCI device: %s\n", addr);
free(ctrlr);
return -1;
}
if (spdk_pci_device_map_bar(pci_dev, 0, &reg_addr, &phys_addr, &size)) {
SPDK_ERRLOG("Failed to allocate BAR0 for NVMe controller: %s\n", addr);
spdk_pci_device_unclaim(pci_dev);
free(ctrlr);
return -1;
}
ctrlr->pci_device = pci_dev;
ctrlr->regs = (volatile struct spdk_nvme_registers *)reg_addr;
TAILQ_INSERT_TAIL(ctrlrs, ctrlr, tailq);
return 0;
}
static void
free_ctrlr(struct nvme_ctrlr *ctrlr)
{
spdk_pci_device_unmap_bar(ctrlr->pci_device, 0, (void *)ctrlr->regs);
spdk_pci_device_unclaim(ctrlr->pci_device);
spdk_pci_device_detach(ctrlr->pci_device);
free(ctrlr);
}
static int
probe_internal(struct spdk_pci_addr *addr, nvme_attach_cb attach_cb, void *cb_ctx)
{
struct nvme_ctrlr *ctrlr, *tmp;
TAILQ_HEAD(, nvme_ctrlr) ctrlrs = TAILQ_HEAD_INITIALIZER(ctrlrs);
int rc;
if (addr == NULL) {
rc = spdk_pci_enumerate(spdk_pci_get_driver("nvme_external"),
pcie_enum_cb, &ctrlrs);
} else {
rc = spdk_pci_device_attach(spdk_pci_get_driver("nvme_external"),
pcie_enum_cb, &ctrlrs, addr);
}
if (rc != 0) {
SPDK_ERRLOG("Failed to enumerate PCI devices\n");
while (!TAILQ_EMPTY(&ctrlrs)) {
ctrlr = TAILQ_FIRST(&ctrlrs);
TAILQ_REMOVE(&ctrlrs, ctrlr, tailq);
free_ctrlr(ctrlr);
}
return rc;
}
TAILQ_FOREACH_SAFE(ctrlr, &ctrlrs, tailq, tmp) {
TAILQ_REMOVE(&ctrlrs, ctrlr, tailq);
TAILQ_INSERT_TAIL(&g_nvme_ctrlrs, ctrlr, tailq);
if (attach_cb != NULL) {
attach_cb(cb_ctx, &ctrlr->pci_device->addr, ctrlr);
}
}
return 0;
}
int
nvme_probe(nvme_attach_cb attach_cb, void *cb_ctx)
{
return probe_internal(NULL, attach_cb, cb_ctx);
}
struct nvme_ctrlr *
nvme_connect(struct spdk_pci_addr *addr)
{
int rc;
rc = probe_internal(addr, NULL, NULL);
if (rc != 0) {
return NULL;
}
return find_ctrlr_by_addr(addr);
}
void
nvme_detach(struct nvme_ctrlr *ctrlr)
{
TAILQ_REMOVE(&g_nvme_ctrlrs, ctrlr, tailq);
free_ctrlr(ctrlr);
}
SPDK_LOG_REGISTER_COMPONENT(nvme_external)