Source-Changes-HG archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

[src/trunk]: src/sys/dev/fdt Listen for PMFE_THROTTLE_* events and limit CPU ...



details:   https://anonhg.NetBSD.org/src/rev/dc902d80bccd
branches:  trunk
changeset: 826903:dc902d80bccd
user:      jmcneill <jmcneill%NetBSD.org@localhost>
date:      Thu Oct 05 01:28:01 2017 +0000

description:
Listen for PMFE_THROTTLE_* events and limit CPU frequency when throttling
is enabled.

diffstat:

 sys/dev/fdt/cpufreq_dt.c |  95 ++++++++++++++++++++++++++++++++++++++++++-----
 1 files changed, 84 insertions(+), 11 deletions(-)

diffs (164 lines):

diff -r 9a45b3557394 -r dc902d80bccd sys/dev/fdt/cpufreq_dt.c
--- a/sys/dev/fdt/cpufreq_dt.c  Thu Oct 05 01:26:53 2017 +0000
+++ b/sys/dev/fdt/cpufreq_dt.c  Thu Oct 05 01:28:01 2017 +0000
@@ -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 <jmcneill%invisible.ca@localhost>
@@ -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 @@
        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 @@
 {
        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 @@
                        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 @@
        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 @@
        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);
 }
 



Home | Main Index | Thread Index | Old Index