Module Name: src Committed By: jmcneill Date: Sat Sep 1 19:36:54 UTC 2018
Modified Files: src/sys/dev/fdt: cpufreq_dt.c Log Message: Add support for opp-v2 tables and multiple instances of the cpufreqdt driver. To generate a diff of this commit: cvs rdiff -u -r1.3 -r1.4 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.3 src/sys/dev/fdt/cpufreq_dt.c:1.4 --- src/sys/dev/fdt/cpufreq_dt.c:1.3 Sat Dec 16 16:41:18 2017 +++ src/sys/dev/fdt/cpufreq_dt.c Sat Sep 1 19:36:53 2018 @@ -1,4 +1,4 @@ -/* $NetBSD: cpufreq_dt.c,v 1.3 2017/12/16 16:41:18 jmcneill Exp $ */ +/* $NetBSD: cpufreq_dt.c,v 1.4 2018/09/01 19:36:53 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.3 2017/12/16 16:41:18 jmcneill Exp $"); +__KERNEL_RCSID(0, "$NetBSD: cpufreq_dt.c,v 1.4 2018/09/01 19:36:53 jmcneill Exp $"); #include <sys/param.h> #include <sys/systm.h> @@ -37,12 +37,24 @@ __KERNEL_RCSID(0, "$NetBSD: cpufreq_dt.c #include <sys/atomic.h> #include <sys/xcall.h> #include <sys/sysctl.h> +#include <sys/queue.h> +#include <sys/once.h> #include <dev/fdt/fdtvar.h> +struct cpufreq_dt_table { + int phandle; + TAILQ_ENTRY(cpufreq_dt_table) next; +}; + +static TAILQ_HEAD(, cpufreq_dt_table) cpufreq_dt_tables = + TAILQ_HEAD_INITIALIZER(cpufreq_dt_tables); +static kmutex_t cpufreq_dt_tables_lock; + struct cpufreq_dt_opp { - u_int freq_khz; - u_int voltage_uv; + u_int freq_khz; + u_int voltage_uv; + u_int latency_ns; }; struct cpufreq_dt_softc { @@ -53,7 +65,6 @@ struct cpufreq_dt_softc { struct cpufreq_dt_opp *sc_opp; ssize_t sc_nopp; - int sc_latency; u_int sc_freq_target; bool sc_freq_throttle; @@ -64,6 +75,8 @@ struct cpufreq_dt_softc { int sc_node_target; int sc_node_current; int sc_node_available; + + struct cpufreq_dt_table sc_table; }; static void @@ -116,6 +129,10 @@ cpufreq_dt_set_rate(struct cpufreq_dt_so if (error != 0) return error; + const u_int latency_us = howmany(opp->latency_ns, 1000); + if (latency_us > 0) + delay(latency_us); + if (sc->sc_supply != NULL) { if (new_uv < old_uv) { error = fdtbus_regulator_set_voltage(sc->sc_supply, @@ -233,11 +250,26 @@ cpufreq_dt_sysctl_helper(SYSCTLFN_ARGS) return error; } +static int +cpufreq_dt_instance_count(void) +{ + deviter_t di; + int count = 0; + + deviter_init(&di, 0); + while (deviter_next(&di) != NULL) + ++count; + deviter_release(&di); + + return count; +} + static void cpufreq_dt_init_sysctl(struct cpufreq_dt_softc *sc) { const struct sysctlnode *node, *cpunode, *freqnode; struct sysctllog *cpufreq_log = NULL; + const char *cpunodename; int error, i; sc->sc_freq_available = kmem_zalloc(strlen("XXXX ") * sc->sc_nopp, KM_SLEEP); @@ -247,13 +279,18 @@ cpufreq_dt_init_sysctl(struct cpufreq_dt strcat(sc->sc_freq_available, buf); } + if (cpufreq_dt_instance_count() > 1) + cpunodename = device_xname(sc->sc_dev); + else + cpunodename = "cpu"; + error = sysctl_createv(&cpufreq_log, 0, NULL, &node, CTLFLAG_PERMANENT, CTLTYPE_NODE, "machdep", NULL, NULL, 0, NULL, 0, CTL_MACHDEP, CTL_EOL); if (error) goto sysctl_failed; error = sysctl_createv(&cpufreq_log, 0, &node, &cpunode, - 0, CTLTYPE_NODE, "cpu", NULL, + 0, CTLTYPE_NODE, cpunodename, NULL, NULL, 0, NULL, 0, CTL_CREATE, CTL_EOL); if (error) goto sysctl_failed; @@ -295,12 +332,77 @@ sysctl_failed: } static int -cpufreq_dt_parse(struct cpufreq_dt_softc *sc) +cpufreq_dt_parse_opp(struct cpufreq_dt_softc *sc) { const int phandle = sc->sc_phandle; const u_int *opp; int len, i; - u_int lat; + + opp = fdtbus_get_prop(phandle, "operating-points", &len); + if (len < 8) + return ENXIO; + + sc->sc_nopp = len / 8; + sc->sc_opp = kmem_zalloc(sizeof(*sc->sc_opp) * sc->sc_nopp, KM_SLEEP); + for (i = 0; i < sc->sc_nopp; i++, opp += 2) { + sc->sc_opp[i].freq_khz = be32toh(opp[0]); + sc->sc_opp[i].voltage_uv = be32toh(opp[1]); + } + + return 0; +} + +static int +cpufreq_dt_parse_opp_v2(struct cpufreq_dt_softc *sc) +{ + const int phandle = sc->sc_phandle; + struct cpufreq_dt_table *table; + uint64_t opp_hz; + uint32_t opp_uv; + int opp_node, i; + + const int opp_table = fdtbus_get_phandle(phandle, "operating-points-v2"); + if (opp_table < 0) + return ENOENT; + + /* If the table is shared, only setup a single instance */ + if (of_hasprop(opp_table, "opp-shared")) { + TAILQ_FOREACH(table, &cpufreq_dt_tables, next) + if (table->phandle == opp_table) + return EEXIST; + sc->sc_table.phandle = opp_table; + TAILQ_INSERT_TAIL(&cpufreq_dt_tables, &sc->sc_table, next); + } + + for (opp_node = OF_child(opp_table); opp_node; opp_node = OF_peer(opp_node)) { + if (fdtbus_status_okay(opp_node)) + sc->sc_nopp++; + } + + if (sc->sc_nopp == 0) + return EINVAL; + + sc->sc_opp = kmem_zalloc(sizeof(*sc->sc_opp) * sc->sc_nopp, KM_SLEEP); + for (opp_node = OF_child(opp_table), i = 0; opp_node; opp_node = OF_peer(opp_node), i++) { + if (!fdtbus_status_okay(opp_node)) + continue; + if (of_getprop_uint64(opp_node, "opp-hz", &opp_hz) != 0) + return EINVAL; + if (of_getprop_uint32(opp_node, "opp-microvolt", &opp_uv) != 0) + return EINVAL; + sc->sc_opp[i].freq_khz = (u_int)(opp_hz / 1000); + sc->sc_opp[i].voltage_uv = opp_uv; + of_getprop_uint32(opp_node, "clock-latency-ns", &sc->sc_opp[i].latency_ns); + } + + return 0; +} + +static int +cpufreq_dt_parse(struct cpufreq_dt_softc *sc) +{ + const int phandle = sc->sc_phandle; + int error, i; if (of_hasprop(phandle, "cpu-supply")) { sc->sc_supply = fdtbus_regulator_acquire(phandle, "cpu-supply"); @@ -316,27 +418,28 @@ cpufreq_dt_parse(struct cpufreq_dt_softc return ENXIO; } - opp = fdtbus_get_prop(phandle, "operating-points", &len); - if (len < 8) - return ENXIO; + mutex_enter(&cpufreq_dt_tables_lock); + if (of_hasprop(phandle, "operating-points")) + error = cpufreq_dt_parse_opp(sc); + else if (of_hasprop(phandle, "operating-points-v2")) + error = cpufreq_dt_parse_opp_v2(sc); + else + error = EINVAL; + mutex_exit(&cpufreq_dt_tables_lock); - sc->sc_nopp = len / 8; - sc->sc_opp = kmem_zalloc(sizeof(*sc->sc_opp) * sc->sc_nopp, KM_SLEEP); - for (i = 0; i < sc->sc_nopp; i++, opp += 2) { - sc->sc_opp[i].freq_khz = be32toh(opp[0]); - sc->sc_opp[i].voltage_uv = be32toh(opp[1]); + if (error) { + aprint_error_dev(sc->sc_dev, + "couldn't parse operating points: %d\n", error); + return error; + } + for (i = 0; i < sc->sc_nopp; i++) { aprint_verbose_dev(sc->sc_dev, "%u.%03u MHz, %u uV\n", sc->sc_opp[i].freq_khz / 1000, sc->sc_opp[i].freq_khz % 1000, sc->sc_opp[i].voltage_uv); } - if (of_getprop_uint32(phandle, "clock-latency", &lat) == 0) - sc->sc_latency = lat; - else - sc->sc_latency = -1; - return 0; } @@ -349,12 +452,12 @@ cpufreq_dt_match(device_t parent, cfdata if (fdtbus_get_reg(phandle, 0, &addr, NULL) != 0) return 0; - /* Generic DT cpufreq driver properties must be defined under /cpus/cpu@0 */ - if (addr != 0) + + if (!of_hasprop(phandle, "clocks")) return 0; - if (!of_hasprop(phandle, "operating-points") || - !of_hasprop(phandle, "clocks")) + if (!of_hasprop(phandle, "operating-points") && + !of_hasprop(phandle, "operating-points-v2")) return 0; return 1; @@ -369,24 +472,34 @@ cpufreq_dt_init(device_t self) if ((error = cpufreq_dt_parse(sc)) != 0) return; + pmf_event_register(sc->sc_dev, PMFE_THROTTLE_ENABLE, cpufreq_dt_throttle_enable, true); + pmf_event_register(sc->sc_dev, PMFE_THROTTLE_DISABLE, cpufreq_dt_throttle_disable, true); + cpufreq_dt_init_sysctl(sc); } +static int +cpufreq_dt_lock_init(void) +{ + mutex_init(&cpufreq_dt_tables_lock, MUTEX_DEFAULT, IPL_NONE); + return 0; +} + static void cpufreq_dt_attach(device_t parent, device_t self, void *aux) { + static ONCE_DECL(locks); struct cpufreq_dt_softc * const sc = device_private(self); struct fdt_attach_args * const faa = aux; + RUN_ONCE(&locks, cpufreq_dt_lock_init); + sc->sc_dev = self; sc->sc_phandle = faa->faa_phandle; 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); }