Date: Wed, 25 Mar 2026 04:46:56 +0000 From: Jaeyoon Choi <jaeyoon@FreeBSD.org> To: src-committers@FreeBSD.org, dev-commits-src-all@FreeBSD.org, dev-commits-src-main@FreeBSD.org Subject: git: e2083e8d3a01 - main - ufshci: Support ACPI Message-ID: <69c368c0.382cc.15737923@gitrepo.freebsd.org>
index | next in thread | raw e-mail
The branch main has been updated by jaeyoon: URL: https://cgit.FreeBSD.org/src/commit/?id=e2083e8d3a01cac739b38521abc72620d3810aba commit e2083e8d3a01cac739b38521abc72620d3810aba Author: Jaeyoon Choi <jaeyoon@FreeBSD.org> AuthorDate: 2026-03-24 05:16:26 +0000 Commit: Jaeyoon Choi <jaeyoon@FreeBSD.org> CommitDate: 2026-03-24 16:45:34 +0000 ufshci: Support ACPI Supports UFS host controller attachment via ACPI. Tested on the Samsung Galaxy Book 4 Edge using Qualcomm Snapdragon X Elite. Additionally, a quirk related to power mode change has been added. For reference, it doesn't reach maximum speed yet. I plan to improve it later. Sponsored by: Samsung Electronics Reviewed by: imp (mentor) Differential Revision: https://reviews.freebsd.org/D55986 --- sys/conf/files | 3 +- sys/dev/ufshci/ufshci_acpi.c | 248 ++++++++++++++++++++++++++++++++++++++++ sys/dev/ufshci/ufshci_ctrlr.c | 71 ++++++++++-- sys/dev/ufshci/ufshci_private.h | 12 +- sys/modules/ufshci/Makefile | 3 + 5 files changed, 324 insertions(+), 13 deletions(-) diff --git a/sys/conf/files b/sys/conf/files index 5fc84f911e19..269f9387ec08 100644 --- a/sys/conf/files +++ b/sys/conf/files @@ -3285,10 +3285,11 @@ dev/uart/uart_tty.c optional uart # Universal Flash Storage Host Controller Interface drivers # dev/ufshci/ufshci.c optional ufshci +dev/ufshci/ufshci_acpi.c optional ufshci acpi dev/ufshci/ufshci_ctrlr.c optional ufshci dev/ufshci/ufshci_ctrlr_cmd.c optional ufshci dev/ufshci/ufshci_dev.c optional ufshci -dev/ufshci/ufshci_pci.c optional ufshci +dev/ufshci/ufshci_pci.c optional ufshci pci dev/ufshci/ufshci_req_queue.c optional ufshci dev/ufshci/ufshci_req_sdb.c optional ufshci dev/ufshci/ufshci_sim.c optional ufshci diff --git a/sys/dev/ufshci/ufshci_acpi.c b/sys/dev/ufshci/ufshci_acpi.c new file mode 100644 index 000000000000..94da0d3cb411 --- /dev/null +++ b/sys/dev/ufshci/ufshci_acpi.c @@ -0,0 +1,248 @@ +/*- + * Copyright (c) 2026, Samsung Electronics Co., Ltd. + * Written by Jaeyoon Choi + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#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 <contrib/dev/acpica/include/acpi.h> + +#include <dev/acpica/acpivar.h> + +#include "ufshci_private.h" + +static int ufshci_acpi_probe(device_t); +static int ufshci_acpi_attach(device_t); +static int ufshci_acpi_detach(device_t); +static int ufshci_acpi_suspend(device_t); +static int ufshci_acpi_resume(device_t); + +static device_method_t ufshci_acpi_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, ufshci_acpi_probe), + DEVMETHOD(device_attach, ufshci_acpi_attach), + DEVMETHOD(device_detach, ufshci_acpi_detach), + DEVMETHOD(device_suspend, ufshci_acpi_suspend), + DEVMETHOD(device_resume, ufshci_acpi_resume), { 0, 0 } +}; + +static driver_t ufshci_acpi_driver = { + "ufshci", + ufshci_acpi_methods, + sizeof(struct ufshci_controller), +}; + +DRIVER_MODULE(ufshci, acpi, ufshci_acpi_driver, 0, 0); +MODULE_DEPEND(ufshci, acpi, 1, 1, 1); + +static struct ufshci_acpi_device { + const char *hid; + const char *desc; + uint32_t ref_clk; + uint32_t quirks; +} ufshci_acpi_devices[] = { + { "QCOM24A5", "Qualcomm Snapdragon X Elite UFS Host Controller", + UFSHCI_REF_CLK_19_2MHz, + UFSHCI_QUIRK_REINIT_AFTER_MAX_GEAR_SWITCH | + UFSHCI_QUIRK_BROKEN_LSDBS_MCQS_CAP }, + { 0x00000000, NULL, 0, 0 } +}; + +static char *ufshci_acpi_ids[] = { "QCOM24A5", NULL }; + +static const struct ufshci_acpi_device * +ufshci_acpi_find_device(device_t dev) +{ + char *hid; + int i; + int rv; + + rv = ACPI_ID_PROBE(device_get_parent(dev), dev, ufshci_acpi_ids, &hid); + if (rv > 0) + return (NULL); + + for (i = 0; ufshci_acpi_devices[i].hid != NULL; i++) { + if (strcmp(ufshci_acpi_devices[i].hid, hid) != 0) + continue; + return (&ufshci_acpi_devices[i]); + } + + return (NULL); +} + +static int +ufshci_acpi_probe(device_t dev) +{ + struct ufshci_controller *ctrlr = device_get_softc(dev); + const struct ufshci_acpi_device *acpi_dev; + + acpi_dev = ufshci_acpi_find_device(dev); + if (acpi_dev == NULL) + return (ENXIO); + + if (acpi_dev->hid) { + ctrlr->quirks = acpi_dev->quirks; + ctrlr->ref_clk = acpi_dev->ref_clk; + } + + if (acpi_dev->desc) { + device_set_desc(dev, acpi_dev->desc); + return (BUS_PROBE_DEFAULT); + } + + return (ENXIO); +} + +static int +ufshci_acpi_allocate_memory(struct ufshci_controller *ctrlr) +{ + ctrlr->resource_id = 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 acpi 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_acpi_setup_shared(struct ufshci_controller *ctrlr) +{ + int error; + + ctrlr->num_io_queues = 1; + ctrlr->rid = 0; + 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_acpi_setup_interrupts(struct ufshci_controller *ctrlr) +{ + int num_io_queues, per_cpu_io_queues, min_cpus_per_ioq; + + /* + * TODO: Need to implement MCQ(Multi Circular Queue) + * Example: num_io_queues = mp_ncpus; + */ + num_io_queues = 1; + 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)); + } + + if (num_io_queues > vm_ndomains) + num_io_queues -= num_io_queues % vm_ndomains; + + ctrlr->num_io_queues = num_io_queues; + return (ufshci_acpi_setup_shared(ctrlr)); +} + +static int +ufshci_acpi_attach(device_t dev) +{ + struct ufshci_controller *ctrlr = device_get_softc(dev); + int status; + + ctrlr->dev = dev; + status = ufshci_acpi_allocate_memory(ctrlr); + if (status != 0) + goto bad; + + status = ufshci_acpi_setup_interrupts(ctrlr); + if (status != 0) + 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); + + return (status); +} + +static int +ufshci_acpi_detach(device_t dev) +{ + return (ufshci_detach(dev)); +} + +static int +ufshci_acpi_suspend(device_t dev) +{ + struct ufshci_controller *ctrlr = device_get_softc(dev); + int error; + + error = bus_generic_suspend(dev); + if (error) + return (error); + + /* Currently, PCI-based ufshci only supports POWER_STYPE_STANDBY */ + error = ufshci_ctrlr_suspend(ctrlr, POWER_STYPE_STANDBY); + return (error); +} + +static int +ufshci_acpi_resume(device_t dev) +{ + struct ufshci_controller *ctrlr = device_get_softc(dev); + int error; + + error = ufshci_ctrlr_resume(ctrlr, POWER_STYPE_AWAKE); + if (error) + return (error); + + error = bus_generic_resume(dev); + return (error); +} diff --git a/sys/dev/ufshci/ufshci_ctrlr.c b/sys/dev/ufshci/ufshci_ctrlr.c index f011d03189e0..244aa723d02a 100644 --- a/sys/dev/ufshci/ufshci_ctrlr.c +++ b/sys/dev/ufshci/ufshci_ctrlr.c @@ -21,6 +21,50 @@ ufshci_ctrlr_fail(struct ufshci_controller *ctrlr) ufshci_req_queue_fail(ctrlr, &ctrlr->transfer_req_queue); } +/* Some controllers require a reinit after switching to the max gear. */ +static int +ufshci_ctrlr_reinit_after_max_gear_switch(struct ufshci_controller *ctrlr) +{ + int error; + + /* Reset device */ + ufshci_utmr_req_queue_disable(ctrlr); + ufshci_utr_req_queue_disable(ctrlr); + + error = ufshci_ctrlr_disable(ctrlr); + if (error != 0) + return (error); + + error = ufshci_ctrlr_enable(ctrlr); + if (error != 0) + return (error); + + error = ufshci_utmr_req_queue_enable(ctrlr); + if (error != 0) + return (error); + + error = ufshci_utr_req_queue_enable(ctrlr); + if (error != 0) + return (error); + + error = ufshci_ctrlr_send_nop(ctrlr); + if (error != 0) + return (error); + + /* Reinit the target device. */ + error = ufshci_dev_init(ctrlr); + if (error != 0) + return (error); + + /* Initialize Reference Clock */ + error = ufshci_dev_init_reference_clock(ctrlr); + if (error != 0) + return (error); + + /* Initialize unipro */ + return (ufshci_dev_init_unipro(ctrlr)); +} + static void ufshci_ctrlr_start(struct ufshci_controller *ctrlr, bool resetting) { @@ -77,6 +121,12 @@ ufshci_ctrlr_start(struct ufshci_controller *ctrlr, bool resetting) ufshci_dev_init_uic_link_state(ctrlr); + if ((ctrlr->quirks & UFSHCI_QUIRK_REINIT_AFTER_MAX_GEAR_SWITCH) && + ufshci_ctrlr_reinit_after_max_gear_switch(ctrlr) != 0) { + ufshci_ctrlr_fail(ctrlr); + return; + } + /* Read Controller Descriptor (Device, Geometry) */ if (ufshci_dev_get_descriptor(ctrlr) != 0) { ufshci_ctrlr_fail(ctrlr); @@ -199,7 +249,7 @@ ufshci_ctrlr_disable(struct ufshci_controller *ctrlr) return (error); } -static int +int ufshci_ctrlr_enable(struct ufshci_controller *ctrlr) { uint32_t ie, hcs; @@ -302,15 +352,18 @@ ufshci_ctrlr_construct(struct ufshci_controller *ctrlr, device_t dev) /* Read Device Capabilities */ ctrlr->cap = cap = ufshci_mmio_read_4(ctrlr, cap); - ctrlr->is_single_db_supported = UFSHCIV(UFSHCI_CAP_REG_LSDBS, cap); - /* - * TODO: This driver does not yet support multi-queue. - * Check the UFSHCI_CAP_REG_MCQS bit in the future to determine if - * multi-queue support is available. - */ - ctrlr->is_mcq_supported = false; - if (!(ctrlr->is_single_db_supported == 0 || ctrlr->is_mcq_supported)) + if (ctrlr->quirks & UFSHCI_QUIRK_BROKEN_LSDBS_MCQS_CAP) { + ctrlr->is_single_db_supported = true; + ctrlr->is_mcq_supported = true; + } else { + ctrlr->is_single_db_supported = (UFSHCIV(UFSHCI_CAP_REG_LSDBS, + cap) == 0); + ctrlr->is_mcq_supported = (UFSHCIV(UFSHCI_CAP_REG_MCQS, cap) == + 1); + } + if (!(ctrlr->is_single_db_supported || ctrlr->is_mcq_supported)) return (ENXIO); + /* * The maximum transfer size supported by UFSHCI spec is 65535 * 256 KiB * However, we limit the maximum transfer size to 1MiB(256 * 4KiB) for diff --git a/sys/dev/ufshci/ufshci_private.h b/sys/dev/ufshci/ufshci_private.h index b25df45d6b75..067b51a419e8 100644 --- a/sys/dev/ufshci/ufshci_private.h +++ b/sys/dev/ufshci/ufshci_private.h @@ -315,10 +315,15 @@ struct ufshci_controller { #define UFSHCI_QUIRK_NOT_SUPPORT_ABORT_TASK \ 16 /* QEMU does not support Task Management Request */ #define UFSHCI_QUIRK_SKIP_WELL_KNOWN_LUNS \ - 32 /* QEMU does not support Well known logical units*/ + 32 /* QEMU does not support Well known logical units */ #define UFSHCI_QUIRK_BROKEN_AUTO_HIBERNATE \ 64 /* Some controllers have the Auto hibernate feature enabled but it \ does not work. */ +#define UFSHCI_QUIRK_REINIT_AFTER_MAX_GEAR_SWITCH \ + 128 /* Some controllers need to reinit the device after gear switch. \ + */ +#define UFSHCI_QUIRK_BROKEN_LSDBS_MCQS_CAP \ + 256 /* Some controllers have their LSDB and MCQS fields reset to 0. */ uint32_t ref_clk; @@ -391,8 +396,8 @@ struct ufshci_controller { /* UFS Transport Protocol Layer (UTP) */ struct ufshci_req_queue task_mgmt_req_queue; struct ufshci_req_queue transfer_req_queue; - bool is_single_db_supported; /* 0 = supported */ - bool is_mcq_supported; /* 1 = supported */ + bool is_single_db_supported; + bool is_mcq_supported; /* UFS Interconnect Layer (UIC) */ struct mtx uic_cmd_lock; @@ -443,6 +448,7 @@ int ufshci_ctrlr_suspend(struct ufshci_controller *ctrlr, int ufshci_ctrlr_resume(struct ufshci_controller *ctrlr, enum power_stype stype); int ufshci_ctrlr_disable(struct ufshci_controller *ctrlr); +int ufshci_ctrlr_enable(struct ufshci_controller *ctrlr); /* ctrlr defined as void * to allow use with config_intrhook. */ void ufshci_ctrlr_start_config_hook(void *arg); void ufshci_ctrlr_poll(struct ufshci_controller *ctrlr); diff --git a/sys/modules/ufshci/Makefile b/sys/modules/ufshci/Makefile index ab5f3eaf88d0..aa0419d3a6d6 100644 --- a/sys/modules/ufshci/Makefile +++ b/sys/modules/ufshci/Makefile @@ -3,6 +3,7 @@ KMOD = ufshci SRCS = ufshci.c \ + ufshci_acpi.c \ ufshci_pci.c \ ufshci_ctrlr.c \ ufshci_dev.c \ @@ -12,8 +13,10 @@ SRCS = ufshci.c \ ufshci_req_sdb.c \ ufshci_sim.c \ ufshci_sysctl.c \ + acpi_if.h \ bus_if.h \ device_if.h \ + opt_acpi.h \ opt_cam.h \ pci_if.hhome | help
Want to link to this message? Use this
URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?69c368c0.382cc.15737923>
