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
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
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
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
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
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
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
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 *
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
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
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