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);
494