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