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