xref: /freebsd/sys/dev/ufshci/ufshci_acpi.c (revision e2083e8d3a01cac739b38521abc72620d3810aba)
1*e2083e8dSJaeyoon Choi /*-
2*e2083e8dSJaeyoon Choi  * Copyright (c) 2026, Samsung Electronics Co., Ltd.
3*e2083e8dSJaeyoon Choi  * Written by Jaeyoon Choi
4*e2083e8dSJaeyoon Choi  *
5*e2083e8dSJaeyoon Choi  * SPDX-License-Identifier: BSD-2-Clause
6*e2083e8dSJaeyoon Choi  */
7*e2083e8dSJaeyoon Choi 
8*e2083e8dSJaeyoon Choi #include <sys/param.h>
9*e2083e8dSJaeyoon Choi #include <sys/systm.h>
10*e2083e8dSJaeyoon Choi #include <sys/buf.h>
11*e2083e8dSJaeyoon Choi #include <sys/bus.h>
12*e2083e8dSJaeyoon Choi #include <sys/conf.h>
13*e2083e8dSJaeyoon Choi #include <sys/proc.h>
14*e2083e8dSJaeyoon Choi #include <sys/smp.h>
15*e2083e8dSJaeyoon Choi 
16*e2083e8dSJaeyoon Choi #include <vm/vm.h>
17*e2083e8dSJaeyoon Choi 
18*e2083e8dSJaeyoon Choi #include <contrib/dev/acpica/include/acpi.h>
19*e2083e8dSJaeyoon Choi 
20*e2083e8dSJaeyoon Choi #include <dev/acpica/acpivar.h>
21*e2083e8dSJaeyoon Choi 
22*e2083e8dSJaeyoon Choi #include "ufshci_private.h"
23*e2083e8dSJaeyoon Choi 
24*e2083e8dSJaeyoon Choi static int ufshci_acpi_probe(device_t);
25*e2083e8dSJaeyoon Choi static int ufshci_acpi_attach(device_t);
26*e2083e8dSJaeyoon Choi static int ufshci_acpi_detach(device_t);
27*e2083e8dSJaeyoon Choi static int ufshci_acpi_suspend(device_t);
28*e2083e8dSJaeyoon Choi static int ufshci_acpi_resume(device_t);
29*e2083e8dSJaeyoon Choi 
30*e2083e8dSJaeyoon Choi static device_method_t ufshci_acpi_methods[] = {
31*e2083e8dSJaeyoon Choi 	/* Device interface */
32*e2083e8dSJaeyoon Choi 	DEVMETHOD(device_probe, ufshci_acpi_probe),
33*e2083e8dSJaeyoon Choi 	DEVMETHOD(device_attach, ufshci_acpi_attach),
34*e2083e8dSJaeyoon Choi 	DEVMETHOD(device_detach, ufshci_acpi_detach),
35*e2083e8dSJaeyoon Choi 	DEVMETHOD(device_suspend, ufshci_acpi_suspend),
36*e2083e8dSJaeyoon Choi 	DEVMETHOD(device_resume, ufshci_acpi_resume), { 0, 0 }
37*e2083e8dSJaeyoon Choi };
38*e2083e8dSJaeyoon Choi 
39*e2083e8dSJaeyoon Choi static driver_t ufshci_acpi_driver = {
40*e2083e8dSJaeyoon Choi 	"ufshci",
41*e2083e8dSJaeyoon Choi 	ufshci_acpi_methods,
42*e2083e8dSJaeyoon Choi 	sizeof(struct ufshci_controller),
43*e2083e8dSJaeyoon Choi };
44*e2083e8dSJaeyoon Choi 
45*e2083e8dSJaeyoon Choi DRIVER_MODULE(ufshci, acpi, ufshci_acpi_driver, 0, 0);
46*e2083e8dSJaeyoon Choi MODULE_DEPEND(ufshci, acpi, 1, 1, 1);
47*e2083e8dSJaeyoon Choi 
48*e2083e8dSJaeyoon Choi static struct ufshci_acpi_device {
49*e2083e8dSJaeyoon Choi 	const char *hid;
50*e2083e8dSJaeyoon Choi 	const char *desc;
51*e2083e8dSJaeyoon Choi 	uint32_t ref_clk;
52*e2083e8dSJaeyoon Choi 	uint32_t quirks;
53*e2083e8dSJaeyoon Choi } ufshci_acpi_devices[] = {
54*e2083e8dSJaeyoon Choi 	{ "QCOM24A5", "Qualcomm Snapdragon X Elite UFS Host Controller",
55*e2083e8dSJaeyoon Choi 	    UFSHCI_REF_CLK_19_2MHz,
56*e2083e8dSJaeyoon Choi 	    UFSHCI_QUIRK_REINIT_AFTER_MAX_GEAR_SWITCH |
57*e2083e8dSJaeyoon Choi 			UFSHCI_QUIRK_BROKEN_LSDBS_MCQS_CAP },
58*e2083e8dSJaeyoon Choi 	{ 0x00000000, NULL, 0, 0 }
59*e2083e8dSJaeyoon Choi };
60*e2083e8dSJaeyoon Choi 
61*e2083e8dSJaeyoon Choi static char *ufshci_acpi_ids[] = { "QCOM24A5", NULL };
62*e2083e8dSJaeyoon Choi 
63*e2083e8dSJaeyoon Choi static const struct ufshci_acpi_device *
ufshci_acpi_find_device(device_t dev)64*e2083e8dSJaeyoon Choi ufshci_acpi_find_device(device_t dev)
65*e2083e8dSJaeyoon Choi {
66*e2083e8dSJaeyoon Choi 	char *hid;
67*e2083e8dSJaeyoon Choi 	int i;
68*e2083e8dSJaeyoon Choi 	int rv;
69*e2083e8dSJaeyoon Choi 
70*e2083e8dSJaeyoon Choi 	rv = ACPI_ID_PROBE(device_get_parent(dev), dev, ufshci_acpi_ids, &hid);
71*e2083e8dSJaeyoon Choi 	if (rv > 0)
72*e2083e8dSJaeyoon Choi 		return (NULL);
73*e2083e8dSJaeyoon Choi 
74*e2083e8dSJaeyoon Choi 	for (i = 0; ufshci_acpi_devices[i].hid != NULL; i++) {
75*e2083e8dSJaeyoon Choi 		if (strcmp(ufshci_acpi_devices[i].hid, hid) != 0)
76*e2083e8dSJaeyoon Choi 			continue;
77*e2083e8dSJaeyoon Choi 		return (&ufshci_acpi_devices[i]);
78*e2083e8dSJaeyoon Choi 	}
79*e2083e8dSJaeyoon Choi 
80*e2083e8dSJaeyoon Choi 	return (NULL);
81*e2083e8dSJaeyoon Choi }
82*e2083e8dSJaeyoon Choi 
83*e2083e8dSJaeyoon Choi static int
ufshci_acpi_probe(device_t dev)84*e2083e8dSJaeyoon Choi ufshci_acpi_probe(device_t dev)
85*e2083e8dSJaeyoon Choi {
86*e2083e8dSJaeyoon Choi 	struct ufshci_controller *ctrlr = device_get_softc(dev);
87*e2083e8dSJaeyoon Choi 	const struct ufshci_acpi_device *acpi_dev;
88*e2083e8dSJaeyoon Choi 
89*e2083e8dSJaeyoon Choi 	acpi_dev = ufshci_acpi_find_device(dev);
90*e2083e8dSJaeyoon Choi 	if (acpi_dev == NULL)
91*e2083e8dSJaeyoon Choi 		return (ENXIO);
92*e2083e8dSJaeyoon Choi 
93*e2083e8dSJaeyoon Choi 	if (acpi_dev->hid) {
94*e2083e8dSJaeyoon Choi 		ctrlr->quirks = acpi_dev->quirks;
95*e2083e8dSJaeyoon Choi 		ctrlr->ref_clk = acpi_dev->ref_clk;
96*e2083e8dSJaeyoon Choi 	}
97*e2083e8dSJaeyoon Choi 
98*e2083e8dSJaeyoon Choi 	if (acpi_dev->desc) {
99*e2083e8dSJaeyoon Choi 		device_set_desc(dev, acpi_dev->desc);
100*e2083e8dSJaeyoon Choi 		return (BUS_PROBE_DEFAULT);
101*e2083e8dSJaeyoon Choi 	}
102*e2083e8dSJaeyoon Choi 
103*e2083e8dSJaeyoon Choi 	return (ENXIO);
104*e2083e8dSJaeyoon Choi }
105*e2083e8dSJaeyoon Choi 
106*e2083e8dSJaeyoon Choi static int
ufshci_acpi_allocate_memory(struct ufshci_controller * ctrlr)107*e2083e8dSJaeyoon Choi ufshci_acpi_allocate_memory(struct ufshci_controller *ctrlr)
108*e2083e8dSJaeyoon Choi {
109*e2083e8dSJaeyoon Choi 	ctrlr->resource_id = 0;
110*e2083e8dSJaeyoon Choi 	ctrlr->resource = bus_alloc_resource_any(ctrlr->dev, SYS_RES_MEMORY,
111*e2083e8dSJaeyoon Choi 	    &ctrlr->resource_id, RF_ACTIVE);
112*e2083e8dSJaeyoon Choi 
113*e2083e8dSJaeyoon Choi 	if (ctrlr->resource == NULL) {
114*e2083e8dSJaeyoon Choi 		ufshci_printf(ctrlr, "unable to allocate acpi resource\n");
115*e2083e8dSJaeyoon Choi 		return (ENOMEM);
116*e2083e8dSJaeyoon Choi 	}
117*e2083e8dSJaeyoon Choi 
118*e2083e8dSJaeyoon Choi 	ctrlr->bus_tag = rman_get_bustag(ctrlr->resource);
119*e2083e8dSJaeyoon Choi 	ctrlr->bus_handle = rman_get_bushandle(ctrlr->resource);
120*e2083e8dSJaeyoon Choi 	ctrlr->regs = (struct ufshci_registers *)ctrlr->bus_handle;
121*e2083e8dSJaeyoon Choi 
122*e2083e8dSJaeyoon Choi 	return (0);
123*e2083e8dSJaeyoon Choi }
124*e2083e8dSJaeyoon Choi 
125*e2083e8dSJaeyoon Choi static int
ufshci_acpi_setup_shared(struct ufshci_controller * ctrlr)126*e2083e8dSJaeyoon Choi ufshci_acpi_setup_shared(struct ufshci_controller *ctrlr)
127*e2083e8dSJaeyoon Choi {
128*e2083e8dSJaeyoon Choi 	int error;
129*e2083e8dSJaeyoon Choi 
130*e2083e8dSJaeyoon Choi 	ctrlr->num_io_queues = 1;
131*e2083e8dSJaeyoon Choi 	ctrlr->rid = 0;
132*e2083e8dSJaeyoon Choi 	ctrlr->res = bus_alloc_resource_any(ctrlr->dev, SYS_RES_IRQ,
133*e2083e8dSJaeyoon Choi 	    &ctrlr->rid, RF_SHAREABLE | RF_ACTIVE);
134*e2083e8dSJaeyoon Choi 	if (ctrlr->res == NULL) {
135*e2083e8dSJaeyoon Choi 		ufshci_printf(ctrlr, "unable to allocate shared interrupt\n");
136*e2083e8dSJaeyoon Choi 		return (ENOMEM);
137*e2083e8dSJaeyoon Choi 	}
138*e2083e8dSJaeyoon Choi 
139*e2083e8dSJaeyoon Choi 	error = bus_setup_intr(ctrlr->dev, ctrlr->res,
140*e2083e8dSJaeyoon Choi 	    INTR_TYPE_MISC | INTR_MPSAFE, NULL, ufshci_ctrlr_shared_handler,
141*e2083e8dSJaeyoon Choi 	    ctrlr, &ctrlr->tag);
142*e2083e8dSJaeyoon Choi 	if (error) {
143*e2083e8dSJaeyoon Choi 		ufshci_printf(ctrlr, "unable to setup shared interrupt\n");
144*e2083e8dSJaeyoon Choi 		return (error);
145*e2083e8dSJaeyoon Choi 	}
146*e2083e8dSJaeyoon Choi 
147*e2083e8dSJaeyoon Choi 	return (0);
148*e2083e8dSJaeyoon Choi }
149*e2083e8dSJaeyoon Choi 
150*e2083e8dSJaeyoon Choi static int
ufshci_acpi_setup_interrupts(struct ufshci_controller * ctrlr)151*e2083e8dSJaeyoon Choi ufshci_acpi_setup_interrupts(struct ufshci_controller *ctrlr)
152*e2083e8dSJaeyoon Choi {
153*e2083e8dSJaeyoon Choi 	int num_io_queues, per_cpu_io_queues, min_cpus_per_ioq;
154*e2083e8dSJaeyoon Choi 
155*e2083e8dSJaeyoon Choi 	/*
156*e2083e8dSJaeyoon Choi 	 * TODO: Need to implement MCQ(Multi Circular Queue)
157*e2083e8dSJaeyoon Choi 	 * Example: num_io_queues = mp_ncpus;
158*e2083e8dSJaeyoon Choi 	 */
159*e2083e8dSJaeyoon Choi 	num_io_queues = 1;
160*e2083e8dSJaeyoon Choi 	TUNABLE_INT_FETCH("hw.ufshci.num_io_queues", &num_io_queues);
161*e2083e8dSJaeyoon Choi 	if (num_io_queues < 1 || num_io_queues > mp_ncpus)
162*e2083e8dSJaeyoon Choi 		num_io_queues = mp_ncpus;
163*e2083e8dSJaeyoon Choi 
164*e2083e8dSJaeyoon Choi 	per_cpu_io_queues = 1;
165*e2083e8dSJaeyoon Choi 	TUNABLE_INT_FETCH("hw.ufshci.per_cpu_io_queues", &per_cpu_io_queues);
166*e2083e8dSJaeyoon Choi 	if (per_cpu_io_queues == 0)
167*e2083e8dSJaeyoon Choi 		num_io_queues = 1;
168*e2083e8dSJaeyoon Choi 
169*e2083e8dSJaeyoon Choi 	min_cpus_per_ioq = smp_threads_per_core;
170*e2083e8dSJaeyoon Choi 	TUNABLE_INT_FETCH("hw.ufshci.min_cpus_per_ioq", &min_cpus_per_ioq);
171*e2083e8dSJaeyoon Choi 	if (min_cpus_per_ioq > 1) {
172*e2083e8dSJaeyoon Choi 		num_io_queues = min(num_io_queues,
173*e2083e8dSJaeyoon Choi 		    max(1, mp_ncpus / min_cpus_per_ioq));
174*e2083e8dSJaeyoon Choi 	}
175*e2083e8dSJaeyoon Choi 
176*e2083e8dSJaeyoon Choi 	if (num_io_queues > vm_ndomains)
177*e2083e8dSJaeyoon Choi 		num_io_queues -= num_io_queues % vm_ndomains;
178*e2083e8dSJaeyoon Choi 
179*e2083e8dSJaeyoon Choi 	ctrlr->num_io_queues = num_io_queues;
180*e2083e8dSJaeyoon Choi 	return (ufshci_acpi_setup_shared(ctrlr));
181*e2083e8dSJaeyoon Choi }
182*e2083e8dSJaeyoon Choi 
183*e2083e8dSJaeyoon Choi static int
ufshci_acpi_attach(device_t dev)184*e2083e8dSJaeyoon Choi ufshci_acpi_attach(device_t dev)
185*e2083e8dSJaeyoon Choi {
186*e2083e8dSJaeyoon Choi 	struct ufshci_controller *ctrlr = device_get_softc(dev);
187*e2083e8dSJaeyoon Choi 	int status;
188*e2083e8dSJaeyoon Choi 
189*e2083e8dSJaeyoon Choi 	ctrlr->dev = dev;
190*e2083e8dSJaeyoon Choi 	status = ufshci_acpi_allocate_memory(ctrlr);
191*e2083e8dSJaeyoon Choi 	if (status != 0)
192*e2083e8dSJaeyoon Choi 		goto bad;
193*e2083e8dSJaeyoon Choi 
194*e2083e8dSJaeyoon Choi 	status = ufshci_acpi_setup_interrupts(ctrlr);
195*e2083e8dSJaeyoon Choi 	if (status != 0)
196*e2083e8dSJaeyoon Choi 		goto bad;
197*e2083e8dSJaeyoon Choi 
198*e2083e8dSJaeyoon Choi 	return (ufshci_attach(dev));
199*e2083e8dSJaeyoon Choi bad:
200*e2083e8dSJaeyoon Choi 	if (ctrlr->resource != NULL) {
201*e2083e8dSJaeyoon Choi 		bus_release_resource(dev, SYS_RES_MEMORY, ctrlr->resource_id,
202*e2083e8dSJaeyoon Choi 		    ctrlr->resource);
203*e2083e8dSJaeyoon Choi 	}
204*e2083e8dSJaeyoon Choi 
205*e2083e8dSJaeyoon Choi 	if (ctrlr->tag)
206*e2083e8dSJaeyoon Choi 		bus_teardown_intr(dev, ctrlr->res, ctrlr->tag);
207*e2083e8dSJaeyoon Choi 
208*e2083e8dSJaeyoon Choi 	if (ctrlr->res)
209*e2083e8dSJaeyoon Choi 		bus_release_resource(dev, SYS_RES_IRQ, rman_get_rid(ctrlr->res),
210*e2083e8dSJaeyoon Choi 		    ctrlr->res);
211*e2083e8dSJaeyoon Choi 
212*e2083e8dSJaeyoon Choi 	return (status);
213*e2083e8dSJaeyoon Choi }
214*e2083e8dSJaeyoon Choi 
215*e2083e8dSJaeyoon Choi static int
ufshci_acpi_detach(device_t dev)216*e2083e8dSJaeyoon Choi ufshci_acpi_detach(device_t dev)
217*e2083e8dSJaeyoon Choi {
218*e2083e8dSJaeyoon Choi 	return (ufshci_detach(dev));
219*e2083e8dSJaeyoon Choi }
220*e2083e8dSJaeyoon Choi 
221*e2083e8dSJaeyoon Choi static int
ufshci_acpi_suspend(device_t dev)222*e2083e8dSJaeyoon Choi ufshci_acpi_suspend(device_t dev)
223*e2083e8dSJaeyoon Choi {
224*e2083e8dSJaeyoon Choi 	struct ufshci_controller *ctrlr = device_get_softc(dev);
225*e2083e8dSJaeyoon Choi 	int error;
226*e2083e8dSJaeyoon Choi 
227*e2083e8dSJaeyoon Choi 	error = bus_generic_suspend(dev);
228*e2083e8dSJaeyoon Choi 	if (error)
229*e2083e8dSJaeyoon Choi 		return (error);
230*e2083e8dSJaeyoon Choi 
231*e2083e8dSJaeyoon Choi 	/* Currently, PCI-based ufshci only supports POWER_STYPE_STANDBY */
232*e2083e8dSJaeyoon Choi 	error = ufshci_ctrlr_suspend(ctrlr, POWER_STYPE_STANDBY);
233*e2083e8dSJaeyoon Choi 	return (error);
234*e2083e8dSJaeyoon Choi }
235*e2083e8dSJaeyoon Choi 
236*e2083e8dSJaeyoon Choi static int
ufshci_acpi_resume(device_t dev)237*e2083e8dSJaeyoon Choi ufshci_acpi_resume(device_t dev)
238*e2083e8dSJaeyoon Choi {
239*e2083e8dSJaeyoon Choi 	struct ufshci_controller *ctrlr = device_get_softc(dev);
240*e2083e8dSJaeyoon Choi 	int error;
241*e2083e8dSJaeyoon Choi 
242*e2083e8dSJaeyoon Choi 	error = ufshci_ctrlr_resume(ctrlr, POWER_STYPE_AWAKE);
243*e2083e8dSJaeyoon Choi 	if (error)
244*e2083e8dSJaeyoon Choi 		return (error);
245*e2083e8dSJaeyoon Choi 
246*e2083e8dSJaeyoon Choi 	error = bus_generic_resume(dev);
247*e2083e8dSJaeyoon Choi 	return (error);
248*e2083e8dSJaeyoon Choi }
249