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