Changeset View
Changeset View
Standalone View
Standalone View
sys/dev/ufshci/ufshci_pci.c
- This file was added.
| /*- | |||||||||
| * SPDX-License-Identifier: BSD-2-Clause | |||||||||
| * | |||||||||
| * Copyright (c) 2025, Samsung Electronics Co., Ltd. | |||||||||
| * All rights reserved. | |||||||||
| * | |||||||||
| * Redistribution and use in source and binary forms, with or without | |||||||||
| * modification, are permitted provided that the following conditions | |||||||||
| * are met: | |||||||||
| * 1. Redistributions of source code must retain the above copyright | |||||||||
| * notice, this list of conditions and the following disclaimer. | |||||||||
| * 2. 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. | |||||||||
| * | |||||||||
| * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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 <sys/param.h> | |||||||||
| #include <sys/systm.h> | |||||||||
| #include <sys/buf.h> | |||||||||
| #include <sys/bus.h> | |||||||||
| #include <sys/conf.h> | |||||||||
| #include <sys/proc.h> | |||||||||
| #include <sys/smp.h> | |||||||||
| #include <vm/vm.h> | |||||||||
| #include <dev/pci/pcireg.h> | |||||||||
| #include <dev/pci/pcivar.h> | |||||||||
| #include "ufshci_private.h" | |||||||||
| static int ufshci_pci_probe(device_t); | |||||||||
| static int ufshci_pci_attach(device_t); | |||||||||
| static int ufshci_pci_detach(device_t); | |||||||||
| static int ufshci_ctrlr_setup_interrupts(struct ufshci_controller *ctrlr); | |||||||||
| static device_method_t ufshci_pci_methods[] = { | |||||||||
| /* Device interface */ | |||||||||
| DEVMETHOD(device_probe, ufshci_pci_probe), | |||||||||
| DEVMETHOD(device_attach, ufshci_pci_attach), | |||||||||
| DEVMETHOD(device_detach, ufshci_pci_detach), | |||||||||
| /* TODO: Implement Suspend, Resume */ | |||||||||
| { 0, 0 } | |||||||||
| }; | |||||||||
| static driver_t ufshci_pci_driver = { | |||||||||
| "ufshci", | |||||||||
| ufshci_pci_methods, | |||||||||
| sizeof(struct ufshci_controller), | |||||||||
| }; | |||||||||
| DRIVER_MODULE(ufshci, pci, ufshci_pci_driver, 0, 0); | |||||||||
| static struct _pcsid | |||||||||
| { | |||||||||
jrtc27: Is there a reason for the underscored name? | |||||||||
Done Inline ActionsI've been copying from nvme_pci.c, which seems to be the convention, but I'm not sure exactly why. jaeyoon: I've been copying from nvme_pci.c, which seems to be the convention, but I'm not sure exactly… | |||||||||
| u_int32_t type; | |||||||||
| const char *desc; | |||||||||
| uint32_t quirks; | |||||||||
| } pci_ids[] = { | |||||||||
| { 0x131b36, "QEMU UFS Host Controller" }, | |||||||||
| { 0x00000000, NULL } | |||||||||
| }; | |||||||||
| static int | |||||||||
| ufshci_pci_probe(device_t device) | |||||||||
| { | |||||||||
| uint32_t type = pci_get_devid(device); | |||||||||
| struct _pcsid *ep = pci_ids; | |||||||||
| while (ep->type && ep->type != type) | |||||||||
| ++ep; | |||||||||
| if (ep->desc) { | |||||||||
| device_set_desc(device, ep->desc); | |||||||||
| return (BUS_PROBE_DEFAULT); | |||||||||
| } | |||||||||
| return (ENXIO); | |||||||||
| } | |||||||||
| static int | |||||||||
| ufshci_ctrlr_allocate_bar(struct ufshci_controller *ctrlr) | |||||||||
| { | |||||||||
| ctrlr->resource_id = PCIR_BAR(0); | |||||||||
| ctrlr->resource = bus_alloc_resource_any(ctrlr->dev, SYS_RES_MEMORY, | |||||||||
| &ctrlr->resource_id, RF_ACTIVE); | |||||||||
| if (ctrlr->resource == NULL) { | |||||||||
| ufshci_printf(ctrlr, "unable to allocate pci resource\n"); | |||||||||
| return (ENOMEM); | |||||||||
| } | |||||||||
| ctrlr->bus_tag = rman_get_bustag(ctrlr->resource); | |||||||||
| ctrlr->bus_handle = rman_get_bushandle(ctrlr->resource); | |||||||||
| ctrlr->regs = (struct ufshci_registers *)ctrlr->bus_handle; | |||||||||
| return (0); | |||||||||
| } | |||||||||
| static int | |||||||||
| ufshci_pci_attach(device_t dev) | |||||||||
| { | |||||||||
| struct ufshci_controller *ctrlr = DEVICE2SOFTC(dev); | |||||||||
| int status; | |||||||||
| ctrlr->dev = dev; | |||||||||
| status = ufshci_ctrlr_allocate_bar(ctrlr); | |||||||||
| if (status != 0) | |||||||||
| goto bad; | |||||||||
| pci_enable_busmaster(dev); | |||||||||
| status = ufshci_ctrlr_setup_interrupts(ctrlr); | |||||||||
| if (status != 0) | |||||||||
Done Inline Actions
jrtc27: | |||||||||
| goto bad; | |||||||||
| return (ufshci_attach(dev)); | |||||||||
| bad: | |||||||||
| if (ctrlr->resource != NULL) { | |||||||||
| bus_release_resource(dev, SYS_RES_MEMORY, | |||||||||
| ctrlr->resource_id, ctrlr->resource); | |||||||||
| } | |||||||||
| if (ctrlr->tag) | |||||||||
| bus_teardown_intr(dev, ctrlr->res, ctrlr->tag); | |||||||||
| if (ctrlr->res) | |||||||||
| bus_release_resource(dev, SYS_RES_IRQ, | |||||||||
| rman_get_rid(ctrlr->res), ctrlr->res); | |||||||||
| if (ctrlr->msi_count > 0) | |||||||||
| pci_release_msi(dev); | |||||||||
| return (status); | |||||||||
| } | |||||||||
| static int | |||||||||
| ufshci_pci_detach(device_t dev) | |||||||||
| { | |||||||||
| struct ufshci_controller *ctrlr = DEVICE2SOFTC(dev); | |||||||||
| int error; | |||||||||
| error = ufshci_detach(dev); | |||||||||
| if (ctrlr->msi_count > 0) | |||||||||
| pci_release_msi(dev); | |||||||||
| pci_disable_busmaster(dev); | |||||||||
| return (error); | |||||||||
| } | |||||||||
| static int | |||||||||
| ufshci_ctrlr_setup_shared(struct ufshci_controller *ctrlr, int rid) | |||||||||
| { | |||||||||
| int error; | |||||||||
| ctrlr->num_io_queues = 1; | |||||||||
| ctrlr->rid = rid; | |||||||||
| ctrlr->res = bus_alloc_resource_any(ctrlr->dev, SYS_RES_IRQ, | |||||||||
| &ctrlr->rid, RF_SHAREABLE | RF_ACTIVE); | |||||||||
| if (ctrlr->res == NULL) { | |||||||||
| ufshci_printf(ctrlr, "unable to allocate shared interrupt\n"); | |||||||||
| return (ENOMEM); | |||||||||
| } | |||||||||
| error = bus_setup_intr(ctrlr->dev, ctrlr->res, | |||||||||
| INTR_TYPE_MISC | INTR_MPSAFE, NULL, ufshci_ctrlr_shared_handler, | |||||||||
| ctrlr, &ctrlr->tag); | |||||||||
| if (error) { | |||||||||
| ufshci_printf(ctrlr, "unable to setup shared interrupt\n"); | |||||||||
| return (error); | |||||||||
| } | |||||||||
| return (0); | |||||||||
| } | |||||||||
| static int | |||||||||
| ufshci_ctrlr_setup_interrupts(struct ufshci_controller *ctrlr) | |||||||||
| { | |||||||||
| device_t dev; | |||||||||
| int force_intx, num_io_queues, per_cpu_io_queues; | |||||||||
| int min_cpus_per_ioq; | |||||||||
| int num_vectors_requested; | |||||||||
| dev = ctrlr->dev; | |||||||||
| force_intx = 0; | |||||||||
| TUNABLE_INT_FETCH("hw.ufshci.force_intx", &force_intx); | |||||||||
Done Inline ActionsCould goto intx and reuse the code at the end of the function (including the printf)? (With ctrlr->msi_count = 0; if not already guaranteed, though it looks like this is called from your attach, and this is your softc, so it should be zero already?) jrtc27: Could `goto intx` and reuse the code at the end of the function (including the printf)? (With… | |||||||||
Done Inline ActionsI applied goto intx. And ctrlr->msi_count is initialized to 0 when allocating softc. jaeyoon: I applied `goto intx`. And `ctrlr->msi_count` is initialized to 0 when allocating softc. | |||||||||
| if (force_intx) | |||||||||
| return (ufshci_ctrlr_setup_shared(ctrlr, 0)); | |||||||||
| if (pci_msix_count(dev) == 0) | |||||||||
| goto msi; | |||||||||
| // For Debug | |||||||||
| ufshci_printf(ctrlr, "ufshci_ctrlr_setup_interrupts: MSI-X\n"); | |||||||||
| /* | |||||||||
| * Try to allocate one MSI-X per core for I/O queues, plus one | |||||||||
| * for admin queue, but accept single shared MSI-X if have to. | |||||||||
| * Fall back to MSI if can't get any MSI-X. | |||||||||
| */ | |||||||||
| num_io_queues = 1; | |||||||||
| /* | |||||||||
| * TODO: Need to implement MCQ(Multi Circular Queue) | |||||||||
| * num_io_queues = mp_ncpus; | |||||||||
| */ | |||||||||
| TUNABLE_INT_FETCH("hw.ufshci.num_io_queues", &num_io_queues); | |||||||||
| if (num_io_queues < 1 || num_io_queues > mp_ncpus) | |||||||||
| num_io_queues = mp_ncpus; | |||||||||
| per_cpu_io_queues = 1; | |||||||||
| TUNABLE_INT_FETCH("hw.ufshci.per_cpu_io_queues", &per_cpu_io_queues); | |||||||||
| if (per_cpu_io_queues == 0) | |||||||||
| num_io_queues = 1; | |||||||||
| min_cpus_per_ioq = smp_threads_per_core; | |||||||||
| TUNABLE_INT_FETCH("hw.ufshci.min_cpus_per_ioq", &min_cpus_per_ioq); | |||||||||
| if (min_cpus_per_ioq > 1) { | |||||||||
| num_io_queues = min(num_io_queues, | |||||||||
| max(1, mp_ncpus / min_cpus_per_ioq)); | |||||||||
| } | |||||||||
| num_io_queues = min(num_io_queues, max(1, pci_msix_count(dev) - 1)); | |||||||||
| again: | |||||||||
| if (num_io_queues > vm_ndomains) | |||||||||
| num_io_queues -= num_io_queues % vm_ndomains; | |||||||||
| num_vectors_requested = min(num_io_queues + 1, pci_msix_count(dev)); | |||||||||
| ctrlr->msi_count = num_vectors_requested; | |||||||||
| if (pci_alloc_msix(dev, &ctrlr->msi_count) != 0) { | |||||||||
| ufshci_printf(ctrlr, "unable to allocate MSI-X\n"); | |||||||||
| ctrlr->msi_count = 0; | |||||||||
| goto msi; | |||||||||
| } | |||||||||
| if (ctrlr->msi_count == 1) | |||||||||
| return (ufshci_ctrlr_setup_shared(ctrlr, 1)); | |||||||||
| if (ctrlr->msi_count != num_vectors_requested) { | |||||||||
| pci_release_msi(dev); | |||||||||
| num_io_queues = ctrlr->msi_count - 1; | |||||||||
| goto again; | |||||||||
| } | |||||||||
| ctrlr->num_io_queues = num_io_queues; | |||||||||
| return (0); | |||||||||
| msi: | |||||||||
| /* | |||||||||
| * Try to allocate 2 MSIs (admin and I/O queues), but accept single | |||||||||
| * shared if have to. Fall back to INTx if can't get any MSI. | |||||||||
| */ | |||||||||
| ctrlr->msi_count = min(pci_msi_count(dev), 2); | |||||||||
| if (ctrlr->msi_count > 0) { | |||||||||
| // For Debug | |||||||||
| ufshci_printf(ctrlr, "ufshci_ctrlr_setup_interrupts: MSI\n"); | |||||||||
| if (pci_alloc_msi(dev, &ctrlr->msi_count) != 0) { | |||||||||
| ufshci_printf(ctrlr, "unable to allocate MSI\n"); | |||||||||
| ctrlr->msi_count = 0; | |||||||||
| } else if (ctrlr->msi_count == 2) { | |||||||||
| ctrlr->num_io_queues = 1; | |||||||||
| return (0); | |||||||||
| } | |||||||||
| } | |||||||||
| // For Debug | |||||||||
| ufshci_printf(ctrlr, "ufshci_ctrlr_setup_interrupts: INTx\n"); | |||||||||
| return (ufshci_ctrlr_setup_shared(ctrlr, ctrlr->msi_count > 0 ? 1 : 0)); | |||||||||
| } | |||||||||
Is there a reason for the underscored name?