Module Name: src Committed By: jmcneill Date: Thu Oct 5 01:28:02 UTC 2017
Modified Files: src/sys/dev/fdt: cpufreq_dt.c Log Message: Listen for PMFE_THROTTLE_* events and limit CPU frequency when throttling is enabled. To generate a diff of this commit: cvs rdiff -u -r1.1 -r1.2 src/sys/dev/fdt/cpufreq_dt.c Please note that diffs are not public domain; they are subject to the copyright notices on the relevant files.
Modified files: Index: src/sys/dev/fdt/cpufreq_dt.c diff -u src/sys/dev/fdt/cpufreq_dt.c:1.1 src/sys/dev/fdt/cpufreq_dt.c:1.2 --- src/sys/dev/fdt/cpufreq_dt.c:1.1 Mon Oct 2 22:49:38 2017 +++ src/sys/dev/fdt/cpufreq_dt.c Thu Oct 5 01:28:01 2017 @@ -1,4 +1,4 @@ -/* $NetBSD: cpufreq_dt.c,v 1.1 2017/10/02 22:49:38 jmcneill Exp $ */ +/* $NetBSD: cpufreq_dt.c,v 1.2 2017/10/05 01:28:01 jmcneill Exp $ */ /*- * Copyright (c) 2015-2017 Jared McNeill <jmcne...@invisible.ca> @@ -27,7 +27,7 @@ */ #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: cpufreq_dt.c,v 1.1 2017/10/02 22:49:38 jmcneill Exp $"); +__KERNEL_RCSID(0, "$NetBSD: cpufreq_dt.c,v 1.2 2017/10/05 01:28:01 jmcneill Exp $"); #include <sys/param.h> #include <sys/systm.h> @@ -55,6 +55,9 @@ struct cpufreq_dt_softc { ssize_t sc_nopp; int sc_latency; + u_int sc_freq_target; + bool sc_freq_throttle; + u_int sc_busy; char *sc_freq_available; @@ -77,6 +80,7 @@ cpufreq_dt_set_rate(struct cpufreq_dt_so { struct cpufreq_dt_opp *opp = NULL; u_int old_rate, new_rate, old_uv, new_uv; + uint64_t xc; int error; ssize_t n; @@ -117,25 +121,86 @@ cpufreq_dt_set_rate(struct cpufreq_dt_so return error; } + if (error == 0) { + xc = xc_broadcast(0, cpufreq_dt_change_cb, sc, NULL); + xc_wait(xc); + + pmf_event_inject(NULL, PMFE_SPEED_CHANGED); + } + return 0; } +static void +cpufreq_dt_throttle_enable(device_t dev) +{ + struct cpufreq_dt_softc * const sc = device_private(dev); + + if (sc->sc_freq_throttle) + return; + + const u_int freq_khz = sc->sc_opp[sc->sc_nopp - 1].freq_khz; + + while (atomic_cas_uint(&sc->sc_busy, 0, 1) != 0) + kpause("throttle", false, 1, NULL); + + if (cpufreq_dt_set_rate(sc, freq_khz) == 0) { + aprint_debug_dev(sc->sc_dev, "throttle enabled (%u.%03u MHz)\n", + freq_khz / 1000, freq_khz % 1000); + sc->sc_freq_throttle = true; + if (sc->sc_freq_target == 0) + sc->sc_freq_target = clk_get_rate(sc->sc_clk) / 1000000; + } + + atomic_dec_uint(&sc->sc_busy); +} + +static void +cpufreq_dt_throttle_disable(device_t dev) +{ + struct cpufreq_dt_softc * const sc = device_private(dev); + + if (!sc->sc_freq_throttle) + return; + + while (atomic_cas_uint(&sc->sc_busy, 0, 1) != 0) + kpause("throttle", false, 1, NULL); + + const u_int freq_khz = sc->sc_freq_target * 1000; + + if (cpufreq_dt_set_rate(sc, freq_khz) == 0) { + aprint_debug_dev(sc->sc_dev, "throttle disabled (%u.%03u MHz)\n", + freq_khz / 1000, freq_khz % 1000); + sc->sc_freq_throttle = false; + } + + atomic_dec_uint(&sc->sc_busy); +} + static int cpufreq_dt_sysctl_helper(SYSCTLFN_ARGS) { struct cpufreq_dt_softc * const sc = rnode->sysctl_data; struct sysctlnode node; u_int fq, oldfq = 0; - uint64_t xc; - int error; + int error, n; node = *rnode; node.sysctl_data = &fq; - fq = clk_get_rate(sc->sc_clk) / 1000000; + if (rnode->sysctl_num == sc->sc_node_target) { + if (sc->sc_freq_target == 0) + sc->sc_freq_target = clk_get_rate(sc->sc_clk) / 1000000; + fq = sc->sc_freq_target; + } else + fq = clk_get_rate(sc->sc_clk) / 1000000; + if (rnode->sysctl_num == sc->sc_node_target) oldfq = fq; + if (sc->sc_freq_target == 0) + sc->sc_freq_target = fq; + error = sysctl_lookup(SYSCTLFN_CALL(&node)); if (error || newp == NULL) return error; @@ -143,16 +208,21 @@ cpufreq_dt_sysctl_helper(SYSCTLFN_ARGS) if (fq == oldfq || rnode->sysctl_num != sc->sc_node_target) return 0; + for (n = 0; n < sc->sc_nopp; n++) + if (sc->sc_opp[n].freq_khz / 1000 == fq) + break; + if (n == sc->sc_nopp) + return EINVAL; + if (atomic_cas_uint(&sc->sc_busy, 0, 1) != 0) return EBUSY; - error = cpufreq_dt_set_rate(sc, fq * 1000); - if (error == 0) { - xc = xc_broadcast(0, cpufreq_dt_change_cb, sc, NULL); - xc_wait(xc); + sc->sc_freq_target = fq; - pmf_event_inject(NULL, PMFE_SPEED_CHANGED); - } + if (sc->sc_freq_throttle) + error = 0; + else + error = cpufreq_dt_set_rate(sc, fq * 1000); atomic_dec_uint(&sc->sc_busy); @@ -308,6 +378,9 @@ cpufreq_dt_attach(device_t parent, devic aprint_naive("\n"); aprint_normal("\n"); + pmf_event_register(self, PMFE_THROTTLE_ENABLE, cpufreq_dt_throttle_enable, true); + pmf_event_register(self, PMFE_THROTTLE_DISABLE, cpufreq_dt_throttle_disable, true); + config_interrupts(self, cpufreq_dt_init); }