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