1 /*-
2 * Copyright (c) 2016 Landon Fuller <landonf@FreeBSD.org>
3 * Copyright (c) 2010 Broadcom Corporation.
4 * Copyright (c) 2017 The FreeBSD Foundation
5 * All rights reserved.
6 *
7 * Portions of this software were developed by Landon Fuller
8 * under sponsorship from the FreeBSD Foundation.
9 *
10 * Portions of this file were derived from the siutils.c source distributed with
11 * the Asus RT-N16 firmware source code release.
12 *
13 * Permission to use, copy, modify, and/or distribute this software for any
14 * purpose with or without fee is hereby granted, provided that the above
15 * copyright notice and this permission notice appear in all copies.
16 *
17 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
18 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
19 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
20 * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
21 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
22 * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
23 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
24 *
25 * $Id: siutils.c,v 1.821.2.48 2011-02-11 20:59:28 Exp $
26 */
27
28 #include <sys/cdefs.h>
29 __FBSDID("$FreeBSD$");
30
31 #include <sys/param.h>
32 #include <sys/kernel.h>
33 #include <sys/bus.h>
34 #include <sys/limits.h>
35 #include <sys/malloc.h>
36 #include <sys/module.h>
37 #include <sys/systm.h>
38
39 #include <dev/bhnd/bhnd.h>
40
41 #include <dev/bhnd/cores/chipc/chipcreg.h>
42 #include <dev/bhnd/cores/chipc/chipcvar.h>
43
44 #include <dev/bhnd/cores/pmu/bhnd_pmuvar.h>
45 #include <dev/bhnd/cores/pmu/bhnd_pmureg.h>
46
47 #include "bhnd_chipc_if.h"
48 #include "bhnd_pwrctl_if.h"
49 #include "bhnd_pwrctl_hostb_if.h"
50
51 #include "bhnd_pwrctl_private.h"
52
53 /*
54 * ChipCommon Power Control.
55 *
56 * Provides a runtime interface to device clocking and power management on
57 * legacy non-PMU chipsets.
58 */
59
60 typedef enum {
61 BHND_PWRCTL_WAR_UP, /**< apply attach/resume workarounds */
62 BHND_PWRCTL_WAR_RUN, /**< apply running workarounds */
63 BHND_PWRCTL_WAR_DOWN, /**< apply detach/suspend workarounds */
64 } bhnd_pwrctl_wars;
65
66 static int bhnd_pwrctl_updateclk(struct bhnd_pwrctl_softc *sc,
67 bhnd_pwrctl_wars wars);
68
69 static struct bhnd_device_quirk pwrctl_quirks[];
70
71 /* Supported parent core device identifiers */
72 static const struct bhnd_device pwrctl_devices[] = {
73 BHND_DEVICE(BCM, CC, "ChipCommon Power Control", pwrctl_quirks),
74 BHND_DEVICE_END
75 };
76
77 /* Device quirks table */
78 static struct bhnd_device_quirk pwrctl_quirks[] = {
79 BHND_CORE_QUIRK (HWREV_LTE(5), PWRCTL_QUIRK_PCICLK_CTL),
80 BHND_CORE_QUIRK (HWREV_RANGE(6, 9), PWRCTL_QUIRK_SLOWCLK_CTL),
81 BHND_CORE_QUIRK (HWREV_RANGE(10, 19), PWRCTL_QUIRK_INSTACLK_CTL),
82
83 BHND_DEVICE_QUIRK_END
84 };
85
86 static int
87 bhnd_pwrctl_probe(device_t dev)
88 {
89 const struct bhnd_device *id;
90 struct chipc_caps *ccaps;
91 device_t chipc;
92
93 /* Look for compatible chipc parent */
94 chipc = device_get_parent(dev);
95 if (device_get_devclass(chipc) != devclass_find("bhnd_chipc"))
96 return (ENXIO);
97
98 if (device_get_driver(chipc) != &bhnd_chipc_driver)
99 return (ENXIO);
100
101 /* Verify chipc capability flags */
102 ccaps = BHND_CHIPC_GET_CAPS(chipc);
103 if (ccaps->pmu || !ccaps->pwr_ctrl)
104 return (ENXIO);
105
106 /* Check for chipc device match */
107 id = bhnd_device_lookup(chipc, pwrctl_devices,
108 sizeof(pwrctl_devices[0]));
109 if (id == NULL)
110 return (ENXIO);
111
112 device_set_desc(dev, id->desc);
113 return (BUS_PROBE_NOWILDCARD);
114 }
115
116 static int
117 bhnd_pwrctl_attach(device_t dev)
118 {
119 struct bhnd_pwrctl_softc *sc;
120 const struct bhnd_chipid *cid;
121 struct chipc_softc *chipc_sc;
122 bhnd_devclass_t hostb_class;
123 device_t hostb_dev;
124 int error;
125
126 sc = device_get_softc(dev);
127
128 sc->dev = dev;
129 sc->chipc_dev = device_get_parent(dev);
130 sc->quirks = bhnd_device_quirks(sc->chipc_dev, pwrctl_devices,
131 sizeof(pwrctl_devices[0]));
132
133 /* On devices that lack a slow clock source, HT must always be
134 * enabled. */
135 hostb_class = BHND_DEVCLASS_INVALID;
136 hostb_dev = bhnd_bus_find_hostb_device(device_get_parent(sc->chipc_dev));
137 if (hostb_dev != NULL)
138 hostb_class = bhnd_get_class(hostb_dev);
139
140 cid = bhnd_get_chipid(sc->chipc_dev);
141 switch (cid->chip_id) {
142 case BHND_CHIPID_BCM4311:
143 if (cid->chip_rev <= 1 && hostb_class == BHND_DEVCLASS_PCI)
144 sc->quirks |= PWRCTL_QUIRK_FORCE_HT;
145 break;
146
147 case BHND_CHIPID_BCM4321:
148 if (hostb_class == BHND_DEVCLASS_PCIE ||
149 hostb_class == BHND_DEVCLASS_PCI)
150 sc->quirks |= PWRCTL_QUIRK_FORCE_HT;
151 break;
152
153 case BHND_CHIPID_BCM4716:
154 if (hostb_class == BHND_DEVCLASS_PCIE)
155 sc->quirks |= PWRCTL_QUIRK_FORCE_HT;
156 break;
157 }
158
159 /* Fetch core register block from ChipCommon parent */
160 chipc_sc = device_get_softc(sc->chipc_dev);
161 sc->res = chipc_sc->core;
162
163 PWRCTL_LOCK_INIT(sc);
164 STAILQ_INIT(&sc->clkres_list);
165
166 /* Initialize power control */
167 PWRCTL_LOCK(sc);
168
169 if ((error = bhnd_pwrctl_init(sc))) {
170 PWRCTL_UNLOCK(sc);
171 goto cleanup;
172 }
173
174 /* Apply default clock transitions */
175 if ((error = bhnd_pwrctl_updateclk(sc, BHND_PWRCTL_WAR_UP))) {
176 PWRCTL_UNLOCK(sc);
177 goto cleanup;
178 }
179
180 PWRCTL_UNLOCK(sc);
181
182 /* Register as the bus PWRCTL provider */
183 if ((error = bhnd_register_provider(dev, BHND_SERVICE_PWRCTL))) {
184 device_printf(sc->dev, "failed to register PWRCTL with bus : "
185 "%d\n", error);
186 goto cleanup;
187 }
188
189 return (0);
190
191 cleanup:
192 PWRCTL_LOCK_DESTROY(sc);
193 return (error);
194 }
195
196 static int
197 bhnd_pwrctl_detach(device_t dev)
198 {
199 struct bhnd_pwrctl_softc *sc;
200 struct bhnd_pwrctl_clkres *clkres, *crnext;
201 int error;
202
203 sc = device_get_softc(dev);
204
205 if ((error = bhnd_deregister_provider(dev, BHND_SERVICE_ANY)))
206 return (error);
207
208 /* Update clock state */
209 PWRCTL_LOCK(sc);
210 error = bhnd_pwrctl_updateclk(sc, BHND_PWRCTL_WAR_DOWN);
211 PWRCTL_UNLOCK(sc);
212 if (error)
213 return (error);
214
215 STAILQ_FOREACH_SAFE(clkres, &sc->clkres_list, cr_link, crnext)
216 free(clkres, M_DEVBUF);
217
218 PWRCTL_LOCK_DESTROY(sc);
219 return (0);
220 }
221
222 static int
223 bhnd_pwrctl_suspend(device_t dev)
224 {
225 struct bhnd_pwrctl_softc *sc;
226 int error;
227
228 sc = device_get_softc(dev);
229
230 /* Update clock state */
231 PWRCTL_LOCK(sc);
232 error = bhnd_pwrctl_updateclk(sc, BHND_PWRCTL_WAR_DOWN);
233 PWRCTL_UNLOCK(sc);
234
235 return (error);
236 }
237
238 static int
239 bhnd_pwrctl_resume(device_t dev)
240 {
241 struct bhnd_pwrctl_softc *sc;
242 int error;
243
244 sc = device_get_softc(dev);
245
246 PWRCTL_LOCK(sc);
247
248 /* Re-initialize power control registers */
249 if ((error = bhnd_pwrctl_init(sc))) {
250 device_printf(sc->dev, "PWRCTL init failed: %d\n", error);
251 goto cleanup;
252 }
253
254 /* Restore clock state */
255 if ((error = bhnd_pwrctl_updateclk(sc, BHND_PWRCTL_WAR_UP))) {
256 device_printf(sc->dev, "clock state restore failed: %d\n",
257 error);
258 goto cleanup;
259 }
260
261 cleanup:
262 PWRCTL_UNLOCK(sc);
263 return (error);
264 }
265
266 static int
267 bhnd_pwrctl_get_clock_latency(device_t dev, bhnd_clock clock,
268 u_int *latency)
269 {
270 struct bhnd_pwrctl_softc *sc = device_get_softc(dev);
271
272 switch (clock) {
273 case BHND_CLOCK_HT:
274 PWRCTL_LOCK(sc);
275 *latency = bhnd_pwrctl_fast_pwrup_delay(sc);
276 PWRCTL_UNLOCK(sc);
277
278 return (0);
279
280 default:
281 return (ENODEV);
282 }
283 }
284
285 static int
286 bhnd_pwrctl_get_clock_freq(device_t dev, bhnd_clock clock, u_int *freq)
287 {
288 struct bhnd_pwrctl_softc *sc = device_get_softc(dev);
289
290 switch (clock) {
291 case BHND_CLOCK_ALP:
292 BPMU_LOCK(sc);
293 *freq = bhnd_pwrctl_getclk_speed(sc);
294 BPMU_UNLOCK(sc);
295
296 return (0);
297
298 case BHND_CLOCK_HT:
299 case BHND_CLOCK_ILP:
300 case BHND_CLOCK_DYN:
301 default:
302 return (ENODEV);
303 }
304 }
305
306 /**
307 * Find the clock reservation associated with @p owner, if any.
308 *
309 * @param sc Driver instance state.
310 * @param owner The owning device.
311 */
312 static struct bhnd_pwrctl_clkres *
313 bhnd_pwrctl_find_res(struct bhnd_pwrctl_softc *sc, device_t owner)
314 {
315 struct bhnd_pwrctl_clkres *clkres;
316
317 PWRCTL_LOCK_ASSERT(sc, MA_OWNED);
318
319 STAILQ_FOREACH(clkres, &sc->clkres_list, cr_link) {
320 if (clkres->owner == owner)
321 return (clkres);
322 }
323
324 /* not found */
325 return (NULL);
326 }
327
328 /**
329 * Enumerate all active clock requests, compute the minimum required clock,
330 * and issue any required clock transition.
331 *
332 * @param sc Driver instance state.
333 * @param wars Work-around state.
334 */
335 static int
336 bhnd_pwrctl_updateclk(struct bhnd_pwrctl_softc *sc, bhnd_pwrctl_wars wars)
337 {
338 struct bhnd_pwrctl_clkres *clkres;
339 bhnd_clock clock;
340
341 PWRCTL_LOCK_ASSERT(sc, MA_OWNED);
342
343 /* Nothing to update on fixed clock devices */
344 if (PWRCTL_QUIRK(sc, FIXED_CLK))
345 return (0);
346
347 /* Default clock target */
348 clock = BHND_CLOCK_DYN;
349
350 /* Apply quirk-specific overrides to the clock target */
351 switch (wars) {
352 case BHND_PWRCTL_WAR_UP:
353 /* Force HT clock */
354 if (PWRCTL_QUIRK(sc, FORCE_HT))
355 clock = BHND_CLOCK_HT;
356 break;
357
358 case BHND_PWRCTL_WAR_RUN:
359 /* Cannot transition clock if FORCE_HT */
360 if (PWRCTL_QUIRK(sc, FORCE_HT))
361 return (0);
362 break;
363
364 case BHND_PWRCTL_WAR_DOWN:
365 /* Leave default clock unmodified to permit
366 * transition back to BHND_CLOCK_DYN on FORCE_HT devices. */
367 break;
368 }
369
370 /* Determine required clock */
371 STAILQ_FOREACH(clkres, &sc->clkres_list, cr_link)
372 clock = bhnd_clock_max(clock, clkres->clock);
373
374 /* Map to supported clock setting */
375 switch (clock) {
376 case BHND_CLOCK_DYN:
377 case BHND_CLOCK_ILP:
378 clock = BHND_CLOCK_DYN;
379 break;
380 case BHND_CLOCK_ALP:
381 /* In theory FORCE_ALP is supported by the hardware, but
382 * there are currently no known use-cases for it; mapping
383 * to HT is still valid, and allows us to punt on determing
384 * where FORCE_ALP is supported and functional */
385 clock = BHND_CLOCK_HT;
386 break;
387 case BHND_CLOCK_HT:
388 break;
389 default:
390 device_printf(sc->dev, "unknown clock: %#x\n", clock);
391 return (ENODEV);
392 }
393
394 /* Issue transition */
395 return (bhnd_pwrctl_setclk(sc, clock));
396 }
397
398 /* BHND_PWRCTL_REQUEST_CLOCK() */
399 static int
400 bhnd_pwrctl_request_clock(device_t dev, device_t child, bhnd_clock clock)
401 {
402 struct bhnd_pwrctl_softc *sc;
403 struct bhnd_pwrctl_clkres *clkres;
404 int error;
405
406 sc = device_get_softc(dev);
407 error = 0;
408
409 PWRCTL_LOCK(sc);
410
411 clkres = bhnd_pwrctl_find_res(sc, child);
412
413 /* BHND_CLOCK_DYN discards the clock reservation entirely */
414 if (clock == BHND_CLOCK_DYN) {
415 /* nothing to clean up? */
416 if (clkres == NULL) {
417 PWRCTL_UNLOCK(sc);
418 return (0);
419 }
420
421 /* drop reservation and apply clock transition */
422 STAILQ_REMOVE(&sc->clkres_list, clkres,
423 bhnd_pwrctl_clkres, cr_link);
424
425 if ((error = bhnd_pwrctl_updateclk(sc, BHND_PWRCTL_WAR_RUN))) {
426 device_printf(dev, "clock transition failed: %d\n",
427 error);
428
429 /* restore reservation */
430 STAILQ_INSERT_TAIL(&sc->clkres_list, clkres, cr_link);
431
432 PWRCTL_UNLOCK(sc);
433 return (error);
434 }
435
436 /* deallocate orphaned reservation */
437 free(clkres, M_DEVBUF);
438
439 PWRCTL_UNLOCK(sc);
440 return (0);
441 }
442
443 /* create (or update) reservation */
444 if (clkres == NULL) {
445 clkres = malloc(sizeof(struct bhnd_pwrctl_clkres), M_DEVBUF,
446 M_NOWAIT);
447 if (clkres == NULL)
448 return (ENOMEM);
449
450 clkres->owner = child;
451 clkres->clock = clock;
452
453 STAILQ_INSERT_TAIL(&sc->clkres_list, clkres, cr_link);
454 } else {
455 KASSERT(clkres->owner == child, ("invalid owner"));
456 clkres->clock = clock;
457 }
458
459 /* apply clock transition */
460 error = bhnd_pwrctl_updateclk(sc, BHND_PWRCTL_WAR_RUN);
461 if (error) {
462 STAILQ_REMOVE(&sc->clkres_list, clkres, bhnd_pwrctl_clkres,
463 cr_link);
464 free(clkres, M_DEVBUF);
465 }
466
467 PWRCTL_UNLOCK(sc);
468 return (error);
469 }
470
471 static device_method_t bhnd_pwrctl_methods[] = {
472 /* Device interface */
473 DEVMETHOD(device_probe, bhnd_pwrctl_probe),
474 DEVMETHOD(device_attach, bhnd_pwrctl_attach),
475 DEVMETHOD(device_detach, bhnd_pwrctl_detach),
476 DEVMETHOD(device_suspend, bhnd_pwrctl_suspend),
477 DEVMETHOD(device_resume, bhnd_pwrctl_resume),
478
479 /* BHND PWRCTL interface */
480 DEVMETHOD(bhnd_pwrctl_request_clock, bhnd_pwrctl_request_clock),
481 DEVMETHOD(bhnd_pwrctl_get_clock_freq, bhnd_pwrctl_get_clock_freq),
482 DEVMETHOD(bhnd_pwrctl_get_clock_latency, bhnd_pwrctl_get_clock_latency),
483
484 DEVMETHOD_END
485 };
486
487 DEFINE_CLASS_0(bhnd_pwrctl, bhnd_pwrctl_driver, bhnd_pwrctl_methods,
488 sizeof(struct bhnd_pwrctl_softc));
489 EARLY_DRIVER_MODULE(bhnd_pwrctl, bhnd_chipc, bhnd_pwrctl_driver,
490 NULL, NULL, BUS_PASS_TIMER + BUS_PASS_ORDER_MIDDLE);
491
492 MODULE_DEPEND(bhnd_pwrctl, bhnd, 1, 1, 1);
493 MODULE_VERSION(bhnd_pwrctl, 1);
Cache object: 14b2bae4735898f3d64bcde2610e8335
|