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