Index: sys/arm/ti/am335x/files.am335x =================================================================== --- sys/arm/ti/am335x/files.am335x +++ sys/arm/ti/am335x/files.am335x @@ -22,3 +22,7 @@ arm/ti/ti_edma3.c standard arm/ti/cpsw/if_cpsw.c optional cpsw + +dev/cpufreq/cpufreq_dt_if.m optional ti_cpufreq fdt +dev/cpufreq/cpufreq_dt.c optional ti_cpufreq fdt +dev/cpufreq/cpufreq_dt_ti_opp.c optional fdt ti_cpufreq Index: sys/dev/cpufreq/cpufreq_dt_ti_opp.c =================================================================== --- /dev/null +++ sys/dev/cpufreq/cpufreq_dt_ti_opp.c @@ -0,0 +1,215 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2023 Oskar Holmlund + * 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. + */ + +/* + * Based on sys/contrib/device-tree/Bindings/cpufreq/ti-cpufreq.txt + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +#include + +#include "cpufreq_dt_if.h" +#include "syscon_if.h" + +#if 0 +#define DPRINTF(dev, msg...) device_printf(dev, "ti_cpufreq: " msg); +#else +#define DPRINTF(dev, msg...) +#endif + +#define AM33XX_EFUSE_OFFSET 0x7FC +#define AM33XX_EFUSE_MASK 0x1FFF +#define AM33XX_REV_OFFSET 0x600 +#define AM33XX_REV_MASK 0xF0000000 +#define AM33XX_REV_SHIFT 28 + +static int +cpufreq_dt_ti_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, len, rv; + uint32_t efuse, rev; + uint32_t opp_sup_hw[2]; + struct syscon *syscon; + + 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); + + rv = ofw_bus_node_is_compatible(opp_table, + "operating-points-v2-ti-cpu"); + if (rv == 0) + { + device_printf(sc->dev, + "Not compatible with operating-points-v2-ti-cpu\n"); + return (ENXIO); + } + + rv = syscon_get_by_ofw_property(sc->dev, opp_table, "syscon", &syscon); + if (rv != 0) { + device_printf(sc->dev, "cannot find syscon\n"); + return (ENXIO); + } + + /* REV see TRM 9.3.1.15 */ + rev = SYSCON_READ_4(syscon, AM33XX_REV_OFFSET) & AM33XX_REV_MASK; + rev >>= AM33XX_REV_SHIFT; + + /* Read EFUSE register see TRM 9.3.1.49 */ + efuse = SYSCON_READ_4(syscon, AM33XX_EFUSE_OFFSET); + efuse &= AM33XX_EFUSE_MASK; + if (efuse == 0) { + DPRINTF(sc->dev, + "Use default - OSD3358 see am335x-osd335x-common.dtsi\n"); + /* All OSD3358 is 1000MHz ZCZ package */ + efuse = 0x1C2F; + } + + /* invert bits + * 0 represents the frequency are supported + * 300MHz zcz bin(0x1FEF) = 0b1111111101111 + * 600MHz zcz bin(0x1FAF) = 0b1111110101111 + * 720MHz zcz bin(0x1F2F) = 0b1111100101111 + * 800MHz zcz bin(0x1E2F) = 0b1111000101111 + * 1000Mhz zcz bin(0x1C2F) = 0b1110000101111 + * + * TODO: if you for some reason use ZCE package + * you have to read package_type bits in the efuse_sma register + * see 9.3.1.49 for details. + */ + efuse = ~efuse; + + 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++) { + /* Check if this hardware support this opp */ + if (OF_hasprop(opp_table, "opp-supported-hw")) { + len = OF_getencprop(opp_table, "opp-supported-hw", + opp_sup_hw, sizeof(opp_sup_hw)); + if (len != sizeof(opp_sup_hw)) + continue; + + if ((rev & opp_sup_hw[0]) == 0) + continue; + if ((efuse & opp_sup_hw[1]) == 0) + continue; + } + /* 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, "found: %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_ti_methods[] = { + /* cpufreq_dt interface */ + DEVMETHOD(cpufreq_dt_oppv2_parse, cpufreq_dt_ti_oppv2_parse), + + DEVMETHOD_END +}; + +extern driver_t cpufreq_dt_driver; + +DEFINE_CLASS_1(cpufreq_dt, cpufreq_dt_ti_driver, cpufreq_dt_ti_methods, + sizeof(struct cpufreq_dt_softc), cpufreq_dt_driver); + +DRIVER_MODULE(cpufreq_dt_ti, cpu, cpufreq_dt_ti_driver, 0, 0); +MODULE_VERSION(cpufreq_dt_ti, 1);