diff --git a/sys/conf/files.arm b/sys/conf/files.arm --- a/sys/conf/files.arm +++ b/sys/conf/files.arm @@ -85,7 +85,9 @@ contrib/openzfs/module/icp/asm-arm/sha2/sha512-armv7.S optional zfs compile-with "${ZFS_S}" crypto/des/des_enc.c optional netsmb +dev/cpufreq/cpufreq_dt_if.m optional cpufreq fdt dev/cpufreq/cpufreq_dt.c optional cpufreq fdt +dev/cpufreq/cpufreq_dt_opp.c optional cpufreq fdt !ti_cpufreq dev/dwc/if_dwc.c optional dwc dev/dwc/if_dwc_if.m optional dwc dev/dwc/dwc1000_core.c optional dwc diff --git a/sys/conf/files.arm64 b/sys/conf/files.arm64 --- a/sys/conf/files.arm64 +++ b/sys/conf/files.arm64 @@ -199,7 +199,9 @@ dev/axgbe/xgbe_osdep.c optional axa fdt dev/axgbe/xgbe-phy-v1.c optional axa fdt +dev/cpufreq/cpufreq_dt_if.m optional cpufreq fdt dev/cpufreq/cpufreq_dt.c optional cpufreq fdt +dev/cpufreq/cpufreq_dt_opp.c optional cpufreq fdt !ti_cpufreq dev/dpaa2/dpaa2_bp.c optional soc_nxp_ls dpaa2 dev/dpaa2/dpaa2_buf.c optional soc_nxp_ls dpaa2 diff --git a/sys/dev/cpufreq/cpufreq_dt.h b/sys/dev/cpufreq/cpufreq_dt.h new file mode 100644 --- /dev/null +++ b/sys/dev/cpufreq/cpufreq_dt.h @@ -0,0 +1,71 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2018 Emmanuel Vadot + * Copyright (c) 2016 Jared McNeill + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef __CPUFREQ_DT_DRV_H__ +#define __CPUFREQ_DT_DRV_H__ + +#include +#include + +#include +#include + +#include +#include + +#define CPUFREQ_DT_HAVE_REGULATOR(sc) ((sc)->reg != NULL) + +enum opp_version { + OPP_V1 = 1, + OPP_V2, +}; + +struct cpufreq_dt_opp { + uint64_t freq; + uint32_t uvolt_target; + uint32_t uvolt_min; + uint32_t uvolt_max; + uint32_t uamps; + uint32_t clk_latency; + bool turbo_mode; + bool opp_suspend; +}; + +struct cpufreq_dt_softc { + device_t dev; + clk_t clk; + regulator_t reg; + + struct cpufreq_dt_opp *opp; + ssize_t nopp; + + int cpu; + cpuset_t cpus; +}; + +#endif /* __CPUFREQ_DT_H__ */ diff --git a/sys/dev/cpufreq/cpufreq_dt.c b/sys/dev/cpufreq/cpufreq_dt.c --- a/sys/dev/cpufreq/cpufreq_dt.c +++ b/sys/dev/cpufreq/cpufreq_dt.c @@ -1,4 +1,6 @@ /*- + * SPDX-License-Identifier: BSD-2-Clause + * * Copyright (c) 2018 Emmanuel Vadot * Copyright (c) 2016 Jared McNeill * @@ -45,6 +47,9 @@ #include #include +#include + +#include "cpufreq_dt_if.h" #include "cpufreq_if.h" #if 0 @@ -53,36 +58,6 @@ #define DPRINTF(dev, msg...) #endif -enum opp_version { - OPP_V1 = 1, - OPP_V2, -}; - -struct cpufreq_dt_opp { - uint64_t freq; - uint32_t uvolt_target; - uint32_t uvolt_min; - uint32_t uvolt_max; - uint32_t uamps; - uint32_t clk_latency; - bool turbo_mode; - bool opp_suspend; -}; - -#define CPUFREQ_DT_HAVE_REGULATOR(sc) ((sc)->reg != NULL) - -struct cpufreq_dt_softc { - device_t dev; - clk_t clk; - regulator_t reg; - - struct cpufreq_dt_opp *opp; - ssize_t nopp; - - int cpu; - cpuset_t cpus; -}; - static void cpufreq_dt_notify(device_t dev, uint64_t freq) { @@ -114,7 +89,8 @@ DPRINTF(dev, "Looking for freq %ju\n", freq); for (n = 0; n < sc->nopp; n++) { diff = abs64((int64_t)sc->opp[n].freq - (int64_t)freq); - DPRINTF(dev, "Testing %ju, diff is %ju\n", sc->opp[n].freq, diff); + DPRINTF(dev, "Testing %ju, diff is %ju\n", + sc->opp[n].freq, diff); if (diff < best_diff) { best_diff = diff; best_n = n; @@ -343,130 +319,11 @@ return (ENXIO); device_set_desc(dev, "Generic cpufreq driver"); - return (BUS_PROBE_GENERIC); -} - -static int -cpufreq_dt_oppv1_parse(struct cpufreq_dt_softc *sc, phandle_t node) -{ - uint32_t *opp, lat; - ssize_t n; - - sc->nopp = OF_getencprop_alloc_multi(node, "operating-points", - sizeof(uint32_t) * 2, (void **)&opp); - if (sc->nopp == -1) - return (ENXIO); - - if (OF_getencprop(node, "clock-latency", &lat, sizeof(lat)) == -1) - lat = CPUFREQ_VAL_UNKNOWN; - - sc->opp = malloc(sizeof(*sc->opp) * sc->nopp, M_DEVBUF, M_WAITOK); - - for (n = 0; n < sc->nopp; n++) { - sc->opp[n].freq = opp[n * 2 + 0] * 1000; - sc->opp[n].uvolt_min = opp[n * 2 + 1]; - sc->opp[n].uvolt_max = sc->opp[n].uvolt_min; - sc->opp[n].uvolt_target = sc->opp[n].uvolt_min; - sc->opp[n].clk_latency = lat; - - if (bootverbose) - device_printf(sc->dev, "%ju.%03ju MHz, %u uV\n", - sc->opp[n].freq / 1000000, - sc->opp[n].freq % 1000000, - sc->opp[n].uvolt_target); - } - free(opp, M_OFWPROP); - - return (0); -} - -static int -cpufreq_dt_oppv2_parse(struct cpufreq_dt_softc *sc, phandle_t node) -{ - phandle_t opp, opp_table, opp_xref; - pcell_t cell[2]; - uint32_t *volts, lat; - int nvolt, i; - - /* - * operating-points-v2 does not require the voltage entries - * and a regulator. So, it's OK if they're not there. - */ - if (OF_getencprop(node, "operating-points-v2", &opp_xref, - sizeof(opp_xref)) == -1) { - device_printf(sc->dev, "Cannot get xref to oppv2 table\n"); - return (ENXIO); - } - - opp_table = OF_node_from_xref(opp_xref); - if (opp_table == opp_xref) - return (ENXIO); - - if (!OF_hasprop(opp_table, "opp-shared")) { - device_printf(sc->dev, "Only opp-shared is supported\n"); - return (ENXIO); - } - for (opp = OF_child(opp_table); opp > 0; opp = OF_peer(opp)) - sc->nopp += 1; + if (bootverbose == 0) + device_quiet(dev); - sc->opp = malloc(sizeof(*sc->opp) * sc->nopp, M_DEVBUF, M_WAITOK); - - for (i = 0, opp_table = OF_child(opp_table); opp_table > 0; - opp_table = OF_peer(opp_table), i++) { - /* opp-hz is a required property */ - if (OF_getencprop(opp_table, "opp-hz", cell, - sizeof(cell)) == -1) - continue; - - sc->opp[i].freq = cell[0]; - sc->opp[i].freq <<= 32; - sc->opp[i].freq |= cell[1]; - - if (OF_getencprop(opp_table, "clock-latency", &lat, - sizeof(lat)) == -1) - sc->opp[i].clk_latency = CPUFREQ_VAL_UNKNOWN; - else - sc->opp[i].clk_latency = (int)lat; - - if (OF_hasprop(opp_table, "turbo-mode")) - sc->opp[i].turbo_mode = true; - if (OF_hasprop(opp_table, "opp-suspend")) - sc->opp[i].opp_suspend = true; - - if (CPUFREQ_DT_HAVE_REGULATOR(sc)) { - nvolt = OF_getencprop_alloc_multi(opp_table, - "opp-microvolt", sizeof(*volts), (void **)&volts); - if (nvolt == 1) { - sc->opp[i].uvolt_target = volts[0]; - sc->opp[i].uvolt_min = volts[0]; - sc->opp[i].uvolt_max = volts[0]; - } else if (nvolt == 3) { - sc->opp[i].uvolt_target = volts[0]; - sc->opp[i].uvolt_min = volts[1]; - sc->opp[i].uvolt_max = volts[2]; - } else { - device_printf(sc->dev, - "Wrong count of opp-microvolt property\n"); - OF_prop_free(volts); - free(sc->opp, M_DEVBUF); - return (ENXIO); - } - OF_prop_free(volts); - } else { - /* No regulator required; don't add anything */ - sc->opp[i].uvolt_target = 0; - sc->opp[i].uvolt_min = 0; - sc->opp[i].uvolt_max = 0; - } - - if (bootverbose) - device_printf(sc->dev, "%ju.%03ju Mhz (%u uV)\n", - sc->opp[i].freq / 1000000, - sc->opp[i].freq % 1000000, - sc->opp[i].uvolt_target); - } - return (0); + return (BUS_PROBE_GENERIC); } static int @@ -478,6 +335,7 @@ int cpu; uint64_t freq; int rv = 0; + ssize_t ret; char device_type[16]; enum opp_version version; @@ -539,7 +397,7 @@ } if (version == OPP_V1) { - rv = cpufreq_dt_oppv1_parse(sc, node); + rv = CPUFREQ_DT_OPPV1_PARSE(dev, sc, node); if (rv != 0) { device_printf(dev, "Failed to parse opp-v1 table\n"); goto error; @@ -547,7 +405,7 @@ OF_getencprop(node, "operating-points", &opp, sizeof(opp)); } else if (version == OPP_V2) { - rv = cpufreq_dt_oppv2_parse(sc, node); + rv = CPUFREQ_DT_OPPV2_PARSE(dev, sc, node); if (rv != 0) { device_printf(dev, "Failed to parse opp-v2 table\n"); goto error; @@ -564,8 +422,12 @@ */ CPU_ZERO(&sc->cpus); cnode = OF_parent(node); - for (cpu = 0, cnode = OF_child(cnode); cnode > 0; cnode = OF_peer(cnode)) { - if (OF_getprop(cnode, "device_type", device_type, sizeof(device_type)) <= 0) + cpu = 0; + for (cnode = OF_child(cnode); cnode > 0; cnode = OF_peer(cnode)) { + + ret = OF_getprop(cnode, "device_type", device_type, + sizeof(device_type)); + if (ret <= 0) continue; if (strcmp(device_type, "cpu") != 0) continue; @@ -584,7 +446,8 @@ OF_getencprop(cnode, "operating-points-v2", &copp, sizeof(copp)); if (opp == copp) { - DPRINTF(dev, "CPU %d is using the same opp as this one (%d)\n", + DPRINTF(dev, + "CPU %d is using the same opp as this one (%d)\n", cpu, sc->cpu); CPU_SET(cpu, &sc->cpus); } @@ -618,11 +481,8 @@ DEVMETHOD_END }; -static driver_t cpufreq_dt_driver = { +driver_t cpufreq_dt_driver = { "cpufreq_dt", cpufreq_dt_methods, sizeof(struct cpufreq_dt_softc), }; - -DRIVER_MODULE(cpufreq_dt, cpu, cpufreq_dt_driver, 0, 0); -MODULE_VERSION(cpufreq_dt, 1); diff --git a/sys/dev/cpufreq/cpufreq_dt_if.m b/sys/dev/cpufreq/cpufreq_dt_if.m new file mode 100644 --- /dev/null +++ b/sys/dev/cpufreq/cpufreq_dt_if.m @@ -0,0 +1,68 @@ +# +# SPDX-License-Identifier: BSD-2-Clause +# +# Copyright (c) 2023 Oskar Holmlund +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# +# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +# + +#include +#include + +INTERFACE cpufreq_dt; + +# cpufreq_dt interface methods +# +# Default methods +# +CODE { + static int + oppv1_parse_default(device_t dev, struct cpufreq_dt_softc *sc, phandle_t node) + { + return (ENXIO); + } + + static int + oppv2_parse_default(device_t dev, struct cpufreq_dt_softc *sc, phandle_t node) + { + return (ENXIO); + } +}; + +# +# Parse oppv1 table. +# +METHOD int oppv1_parse { + device_t dev; + struct cpufreq_dt_softc *sc; + phandle_t node; +} DEFAULT oppv1_parse_default; + +# +# Parse oppv2 table. +# +METHOD int oppv2_parse { + device_t dev; + struct cpufreq_dt_softc *sc; + phandle_t node; +} DEFAULT oppv2_parse_default; diff --git a/sys/dev/cpufreq/cpufreq_dt_opp.c b/sys/dev/cpufreq/cpufreq_dt_opp.c new file mode 100644 --- /dev/null +++ b/sys/dev/cpufreq/cpufreq_dt_opp.c @@ -0,0 +1,192 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2018 Emmanuel Vadot + * Copyright (c) 2016 Jared McNeill + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* + * Generic DT based cpufreq driver + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include + +#include + +#include "cpufreq_dt_if.h" +#include "cpufreq_if.h" + +static int +cpufreq_dt_oppv1_parse(device_t dev, struct cpufreq_dt_softc *sc, phandle_t node) +{ + uint32_t *opp, lat; + ssize_t n; + + sc->nopp = OF_getencprop_alloc_multi(node, "operating-points", + sizeof(uint32_t) * 2, (void **)&opp); + if (sc->nopp == -1) + return (ENXIO); + + if (OF_getencprop(node, "clock-latency", &lat, sizeof(lat)) == -1) + lat = CPUFREQ_VAL_UNKNOWN; + + sc->opp = malloc(sizeof(*sc->opp) * sc->nopp, M_DEVBUF, M_WAITOK); + + for (n = 0; n < sc->nopp; n++) { + sc->opp[n].freq = opp[n * 2 + 0] * 1000; + sc->opp[n].uvolt_min = opp[n * 2 + 1]; + sc->opp[n].uvolt_max = sc->opp[n].uvolt_min; + sc->opp[n].uvolt_target = sc->opp[n].uvolt_min; + sc->opp[n].clk_latency = lat; + + if (bootverbose) + device_printf(sc->dev, "%ju.%03ju MHz, %u uV\n", + sc->opp[n].freq / 1000000, + sc->opp[n].freq % 1000000, + sc->opp[n].uvolt_target); + } + free(opp, M_OFWPROP); + + return (0); +} + +static int +cpufreq_dt_oppv2_parse(device_t dev, struct cpufreq_dt_softc *sc, phandle_t node) +{ + phandle_t opp, opp_table, opp_xref; + pcell_t cell[2]; + uint32_t *volts, lat; + int nvolt, i; + + /* + * operating-points-v2 does not require the voltage entries + * and a regulator. So, it's OK if they're not there. + */ + if (OF_getencprop(node, "operating-points-v2", &opp_xref, + sizeof(opp_xref)) == -1) { + device_printf(sc->dev, "Cannot get xref to oppv2 table\n"); + return (ENXIO); + } + + opp_table = OF_node_from_xref(opp_xref); + if (opp_table == opp_xref) + return (ENXIO); + + if (!OF_hasprop(opp_table, "opp-shared")) { + device_printf(sc->dev, "Only opp-shared is supported\n"); + return (ENXIO); + } + + for (opp = OF_child(opp_table); opp > 0; opp = OF_peer(opp)) + sc->nopp += 1; + + sc->opp = malloc(sizeof(*sc->opp) * sc->nopp, M_DEVBUF, M_WAITOK); + + for (i = 0, opp_table = OF_child(opp_table); opp_table > 0; + opp_table = OF_peer(opp_table), i++) { + /* opp-hz is a required property */ + if (OF_getencprop(opp_table, "opp-hz", cell, + sizeof(cell)) == -1) + continue; + + sc->opp[i].freq = cell[0]; + sc->opp[i].freq <<= 32; + sc->opp[i].freq |= cell[1]; + + if (OF_getencprop(opp_table, "clock-latency", &lat, + sizeof(lat)) == -1) + sc->opp[i].clk_latency = CPUFREQ_VAL_UNKNOWN; + else + sc->opp[i].clk_latency = (int)lat; + + if (OF_hasprop(opp_table, "turbo-mode")) + sc->opp[i].turbo_mode = true; + if (OF_hasprop(opp_table, "opp-suspend")) + sc->opp[i].opp_suspend = true; + + if (CPUFREQ_DT_HAVE_REGULATOR(sc)) { + nvolt = OF_getencprop_alloc_multi(opp_table, + "opp-microvolt", sizeof(*volts), (void **)&volts); + if (nvolt == 1) { + sc->opp[i].uvolt_target = volts[0]; + sc->opp[i].uvolt_min = volts[0]; + sc->opp[i].uvolt_max = volts[0]; + } else if (nvolt == 3) { + sc->opp[i].uvolt_target = volts[0]; + sc->opp[i].uvolt_min = volts[1]; + sc->opp[i].uvolt_max = volts[2]; + } else { + device_printf(sc->dev, + "Wrong count of opp-microvolt property\n"); + OF_prop_free(volts); + free(sc->opp, M_DEVBUF); + return (ENXIO); + } + OF_prop_free(volts); + } else { + /* No regulator required; don't add anything */ + sc->opp[i].uvolt_target = 0; + sc->opp[i].uvolt_min = 0; + sc->opp[i].uvolt_max = 0; + } + + if (bootverbose) + device_printf(sc->dev, "%ju.%03ju Mhz (%u uV)\n", + sc->opp[i].freq / 1000000, + sc->opp[i].freq % 1000000, + sc->opp[i].uvolt_target); + } + return (0); +} + +static device_method_t cpufreq_dt_opp_methods[] = { + /* cpufreq_dt interface */ + DEVMETHOD(cpufreq_dt_oppv1_parse, cpufreq_dt_oppv1_parse), + DEVMETHOD(cpufreq_dt_oppv2_parse, cpufreq_dt_oppv2_parse), + + DEVMETHOD_END +}; + +extern driver_t cpufreq_dt_driver; + +DEFINE_CLASS_1(cpufreq_dt, cpufreq_dt_opp_driver, cpufreq_dt_opp_methods, + sizeof(struct cpufreq_dt_softc), cpufreq_dt_driver); + +DRIVER_MODULE(cpufreq_dt, cpu, cpufreq_dt_opp_driver, 0, 0); +MODULE_VERSION(cpufreq_dt, 1);