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