2 * Copyright (c) 2015-2016 Landon Fuller <landon@landonf.org>
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer,
10 * without modification.
11 * 2. Redistributions in binary form must reproduce at minimum a disclaimer
12 * similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any
13 * redistribution must be conditioned upon including a substantially
14 * similar Disclaimer requirement for further binary redistribution.
17 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19 * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY
20 * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
21 * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY,
22 * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
25 * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
27 * THE POSSIBILITY OF SUCH DAMAGES.
30 #include <sys/cdefs.h>
31 __FBSDID("$FreeBSD$");
33 #include <sys/param.h>
34 #include <sys/kernel.h>
37 #include <sys/malloc.h>
38 #include <sys/module.h>
39 #include <sys/mutex.h>
40 #include <sys/sysctl.h>
41 #include <sys/systm.h>
43 #include <machine/bus.h>
44 #include <machine/resource.h>
46 #include <dev/bhnd/bhnd.h>
47 #include <dev/bhnd/cores/chipc/chipc.h>
49 #include "bhnd_nvram_map.h"
51 #include "bhnd_pmureg.h"
52 #include "bhnd_pmuvar.h"
54 #include "bhnd_pmu_private.h"
57 * Broadcom PMU driver.
59 * On modern BHND chipsets, the PMU, GCI, and SRENG (Save/Restore Engine?)
60 * register blocks are found within a dedicated PMU core (attached via
61 * the AHB 'always on bus').
63 * On earlier chipsets, these register blocks are found at the same
64 * offsets within the ChipCommon core.
67 devclass_t bhnd_pmu_devclass; /**< bhnd(4) PMU device class */
69 static int bhnd_pmu_sysctl_bus_freq(SYSCTL_HANDLER_ARGS);
70 static int bhnd_pmu_sysctl_cpu_freq(SYSCTL_HANDLER_ARGS);
71 static int bhnd_pmu_sysctl_mem_freq(SYSCTL_HANDLER_ARGS);
73 static uint32_t bhnd_pmu_read_4(bus_size_t reg, void *ctx);
74 static void bhnd_pmu_write_4(bus_size_t reg, uint32_t val, void *ctx);
75 static uint32_t bhnd_pmu_read_chipst(void *ctx);
77 static const struct bhnd_pmu_io bhnd_pmu_res_io = {
78 .rd4 = bhnd_pmu_read_4,
79 .wr4 = bhnd_pmu_write_4,
80 .rd_chipst = bhnd_pmu_read_chipst
83 #define BPMU_ASSERT_CLKCTL_AVAIL(_pinfo) \
84 KASSERT(!bhnd_is_hw_suspended((_pinfo)->pm_dev), \
85 ("reading clkctl on suspended core will trigger system livelock"))
87 #define BPMU_CLKCTL_READ_4(_pinfo) \
88 bhnd_bus_read_4((_pinfo)->pm_res, (_pinfo)->pm_regs)
90 #define BPMU_CLKCTL_WRITE_4(_pinfo, _val) \
91 bhnd_bus_write_4((_pinfo)->pm_res, (_pinfo)->pm_regs, (_val))
93 #define BPMU_CLKCTL_SET_4(_pinfo, _val, _mask) \
94 BPMU_CLKCTL_WRITE_4((_pinfo), \
95 ((_val) & (_mask)) | (BPMU_CLKCTL_READ_4(_pinfo) & ~(_mask)))
98 * Default bhnd_pmu driver implementation of DEVICE_PROBE().
101 bhnd_pmu_probe(device_t dev)
103 return (BUS_PROBE_DEFAULT);
107 * Default bhnd_pmu driver implementation of DEVICE_ATTACH().
109 * @param dev PMU device.
110 * @param res The PMU device registers. The driver will maintain a borrowed
111 * reference to this resource for the lifetime of the device.
114 bhnd_pmu_attach(device_t dev, struct bhnd_resource *res)
116 struct bhnd_pmu_softc *sc;
117 struct sysctl_ctx_list *ctx;
118 struct sysctl_oid *tree;
119 devclass_t bhnd_class;
123 sc = device_get_softc(dev);
128 /* Fetch capability flags */
129 sc->caps = bhnd_bus_read_4(sc->res, BHND_PMU_CAP);
131 /* Find the bus-attached core */
132 bhnd_class = devclass_find("bhnd");
134 while ((bus = device_get_parent(core)) != NULL) {
135 if (device_get_devclass(bus) == bhnd_class)
142 device_printf(sc->dev, "bhnd bus not found\n");
146 /* Fetch chip and board info */
147 sc->cid = *bhnd_get_chipid(core);
149 if ((error = bhnd_read_board_info(core, &sc->board))) {
150 device_printf(sc->dev, "error fetching board info: %d\n",
155 /* Locate ChipCommon device */
156 sc->chipc_dev = bhnd_find_child(bus, BHND_DEVCLASS_CC, 0);
157 if (sc->chipc_dev == NULL) {
158 device_printf(sc->dev, "chipcommon device not found\n");
162 /* Initialize query state */
163 error = bhnd_pmu_query_init(&sc->query, dev, sc->cid, &bhnd_pmu_res_io,
167 sc->io = sc->query.io;
168 sc->io_ctx = sc->query.io_ctx;
172 /* Set quirk flags */
173 switch (sc->cid.chip_id) {
174 case BHND_CHIPID_BCM4328:
175 case BHND_CHIPID_BCM5354:
176 /* HTAVAIL/ALPAVAIL are bitswapped in CLKCTL */
177 sc->quirks |= BPMU_QUIRK_CLKCTL_CCS0;
184 if ((error = bhnd_pmu_init(sc))) {
185 device_printf(sc->dev, "PMU init failed: %d\n", error);
189 /* Set up sysctl nodes */
190 ctx = device_get_sysctl_ctx(dev);
191 tree = device_get_sysctl_tree(dev);
193 SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
194 "bus_freq", CTLTYPE_UINT | CTLFLAG_RD, sc, 0,
195 bhnd_pmu_sysctl_bus_freq, "IU", "Bus clock frequency");
197 SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
198 "cpu_freq", CTLTYPE_UINT | CTLFLAG_RD, sc, 0,
199 bhnd_pmu_sysctl_cpu_freq, "IU", "CPU clock frequency");
201 SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
202 "mem_freq", CTLTYPE_UINT | CTLFLAG_RD, sc, 0,
203 bhnd_pmu_sysctl_mem_freq, "IU", "Memory clock frequency");
208 BPMU_LOCK_DESTROY(sc);
209 bhnd_pmu_query_fini(&sc->query);
214 * Default bhnd_pmu driver implementation of DEVICE_DETACH().
217 bhnd_pmu_detach(device_t dev)
219 struct bhnd_pmu_softc *sc;
221 sc = device_get_softc(dev);
223 BPMU_LOCK_DESTROY(sc);
224 bhnd_pmu_query_fini(&sc->query);
230 * Default bhnd_pmu driver implementation of DEVICE_SUSPEND().
233 bhnd_pmu_suspend(device_t dev)
239 * Default bhnd_pmu driver implementation of DEVICE_RESUME().
242 bhnd_pmu_resume(device_t dev)
244 struct bhnd_pmu_softc *sc;
247 sc = device_get_softc(dev);
249 /* Re-initialize PMU */
250 if ((error = bhnd_pmu_init(sc))) {
251 device_printf(sc->dev, "PMU init failed: %d\n", error);
259 bhnd_pmu_sysctl_bus_freq(SYSCTL_HANDLER_ARGS)
261 struct bhnd_pmu_softc *sc;
267 freq = bhnd_pmu_si_clock(&sc->query);
270 return (sysctl_handle_32(oidp, NULL, freq, req));
274 bhnd_pmu_sysctl_cpu_freq(SYSCTL_HANDLER_ARGS)
276 struct bhnd_pmu_softc *sc;
282 freq = bhnd_pmu_cpu_clock(&sc->query);
285 return (sysctl_handle_32(oidp, NULL, freq, req));
289 bhnd_pmu_sysctl_mem_freq(SYSCTL_HANDLER_ARGS)
291 struct bhnd_pmu_softc *sc;
297 freq = bhnd_pmu_mem_clock(&sc->query);
300 return (sysctl_handle_32(oidp, NULL, freq, req));
304 bhnd_pmu_core_req_clock(device_t dev, struct bhnd_core_pmu_info *pinfo,
307 struct bhnd_pmu_softc *sc;
311 BPMU_ASSERT_CLKCTL_AVAIL(pinfo);
313 sc = device_get_softc(dev);
322 req |= BHND_CCS_FORCEILP;
325 req |= BHND_CCS_FORCEALP;
326 avail |= BHND_CCS_ALPAVAIL;
329 req |= BHND_CCS_FORCEHT;
330 avail |= BHND_CCS_HTAVAIL;
333 device_printf(dev, "%s requested unknown clock: %#x\n",
334 device_get_nameunit(pinfo->pm_dev), clock);
341 BPMU_CLKCTL_SET_4(pinfo, req, BHND_CCS_FORCE_MASK);
343 /* Wait for clock availability */
344 bhnd_pmu_wait_clkst(sc, pinfo->pm_dev, pinfo->pm_res, pinfo->pm_regs,
353 bhnd_pmu_core_en_clocks(device_t dev, struct bhnd_core_pmu_info *pinfo,
356 struct bhnd_pmu_softc *sc;
360 BPMU_ASSERT_CLKCTL_AVAIL(pinfo);
362 sc = device_get_softc(dev);
367 /* Build clock request flags */
368 if (clocks & BHND_CLOCK_DYN) /* nothing to enable */
369 clocks &= ~BHND_CLOCK_DYN;
371 if (clocks & BHND_CLOCK_ILP) /* nothing to enable */
372 clocks &= ~BHND_CLOCK_ILP;
374 if (clocks & BHND_CLOCK_ALP) {
375 req |= BHND_CCS_ALPAREQ;
376 avail |= BHND_CCS_ALPAVAIL;
377 clocks &= ~BHND_CLOCK_ALP;
380 if (clocks & BHND_CLOCK_HT) {
381 req |= BHND_CCS_HTAREQ;
382 avail |= BHND_CCS_HTAVAIL;
383 clocks &= ~BHND_CLOCK_HT;
386 /* Check for unknown clock values */
388 device_printf(dev, "%s requested unknown clocks: %#x\n",
389 device_get_nameunit(pinfo->pm_dev), clocks);
396 BPMU_CLKCTL_SET_4(pinfo, req, BHND_CCS_AREQ_MASK);
398 /* Wait for clock availability */
399 bhnd_pmu_wait_clkst(sc, pinfo->pm_dev, pinfo->pm_res, pinfo->pm_regs,
408 bhnd_pmu_core_req_ext_rsrc(device_t dev, struct bhnd_core_pmu_info *pinfo,
411 struct bhnd_pmu_softc *sc;
415 BPMU_ASSERT_CLKCTL_AVAIL(pinfo);
417 sc = device_get_softc(dev);
419 if (rsrc > BHND_CCS_ERSRC_MAX)
422 req = BHND_PMU_SET_BITS((1<<rsrc), BHND_CCS_ERSRC_REQ);
423 avail = BHND_PMU_SET_BITS((1<<rsrc), BHND_CCS_ERSRC_STS);
428 BPMU_CLKCTL_SET_4(pinfo, req, req);
430 /* Wait for resource availability */
431 bhnd_pmu_wait_clkst(sc, pinfo->pm_dev, pinfo->pm_res, pinfo->pm_regs,
440 bhnd_pmu_core_release_ext_rsrc(device_t dev, struct bhnd_core_pmu_info *pinfo,
443 struct bhnd_pmu_softc *sc;
446 BPMU_ASSERT_CLKCTL_AVAIL(pinfo);
448 sc = device_get_softc(dev);
450 if (rsrc > BHND_CCS_ERSRC_MAX)
453 mask = BHND_PMU_SET_BITS((1<<rsrc), BHND_CCS_ERSRC_REQ);
457 BPMU_CLKCTL_SET_4(pinfo, 0x0, mask);
464 bhnd_pmu_core_release(device_t dev, struct bhnd_core_pmu_info *pinfo)
466 struct bhnd_pmu_softc *sc;
468 sc = device_get_softc(dev);
470 /* On PMU-equipped hardware, clkctl is cleared on RESET (and
471 * attempting to access it will trigger a system livelock). */
472 if (bhnd_is_hw_suspended(pinfo->pm_dev))
477 /* Clear all FORCE, AREQ, and ERSRC flags */
478 BPMU_CLKCTL_SET_4(pinfo, 0x0,
479 BHND_CCS_FORCE_MASK | BHND_CCS_AREQ_MASK | BHND_CCS_ERSRC_REQ_MASK);
487 bhnd_pmu_read_4(bus_size_t reg, void *ctx)
489 struct bhnd_pmu_softc *sc = ctx;
490 return (bhnd_bus_read_4(sc->res, reg));
494 bhnd_pmu_write_4(bus_size_t reg, uint32_t val, void *ctx)
496 struct bhnd_pmu_softc *sc = ctx;
497 return (bhnd_bus_write_4(sc->res, reg, val));
501 bhnd_pmu_read_chipst(void *ctx)
503 struct bhnd_pmu_softc *sc = ctx;
504 return (BHND_CHIPC_READ_CHIPST(sc->chipc_dev));
507 static device_method_t bhnd_pmu_methods[] = {
508 /* Device interface */
509 DEVMETHOD(device_probe, bhnd_pmu_probe),
510 DEVMETHOD(device_detach, bhnd_pmu_detach),
511 DEVMETHOD(device_suspend, bhnd_pmu_suspend),
512 DEVMETHOD(device_resume, bhnd_pmu_resume),
514 /* BHND PMU interface */
515 DEVMETHOD(bhnd_pmu_core_req_clock, bhnd_pmu_core_req_clock),
516 DEVMETHOD(bhnd_pmu_core_en_clocks, bhnd_pmu_core_en_clocks),
517 DEVMETHOD(bhnd_pmu_core_req_ext_rsrc, bhnd_pmu_core_req_ext_rsrc),
518 DEVMETHOD(bhnd_pmu_core_release_ext_rsrc, bhnd_pmu_core_release_ext_rsrc),
519 DEVMETHOD(bhnd_pmu_core_release, bhnd_pmu_core_release),
524 DEFINE_CLASS_0(bhnd_pmu, bhnd_pmu_driver, bhnd_pmu_methods, sizeof(struct bhnd_pmu_softc));
525 MODULE_VERSION(bhnd_pmu, 1);