From 2d72e67d3bada9b7643da63a7d6e08ed201d1fc3 Mon Sep 17 00:00:00 2001 From: drahn Date: Sat, 1 Jul 2023 16:34:29 +0000 Subject: [PATCH] Add driver for QC cpu Power States. ok kettenis@ patrick@ --- sys/arch/arm64/conf/GENERIC | 3 +- sys/dev/fdt/files.fdt | 7 +- sys/dev/fdt/qccpu.c | 254 ++++++++++++++++++++++++++++++++++++ 3 files changed, 262 insertions(+), 2 deletions(-) create mode 100644 sys/dev/fdt/qccpu.c diff --git a/sys/arch/arm64/conf/GENERIC b/sys/arch/arm64/conf/GENERIC index ceec2009012..132b049a028 100644 --- a/sys/arch/arm64/conf/GENERIC +++ b/sys/arch/arm64/conf/GENERIC @@ -1,4 +1,4 @@ -# $OpenBSD: GENERIC,v 1.274 2023/06/27 22:38:46 patrick Exp $ +# $OpenBSD: GENERIC,v 1.275 2023/07/01 16:34:29 drahn Exp $ # # GENERIC machine description file # @@ -321,6 +321,7 @@ sdmmc* at dwmshc? # Qualcomm SoCs qcaoss* at fdt? +qccpu* at fdt? qcdwusb* at fdt? qcgpio* at acpi? qcgpio* at fdt? early 1 diff --git a/sys/dev/fdt/files.fdt b/sys/dev/fdt/files.fdt index 9d2da890eaa..fa34dd44f20 100644 --- a/sys/dev/fdt/files.fdt +++ b/sys/dev/fdt/files.fdt @@ -1,4 +1,4 @@ -# $OpenBSD: files.fdt,v 1.194 2023/06/27 22:38:46 patrick Exp $ +# $OpenBSD: files.fdt,v 1.195 2023/07/01 16:34:30 drahn Exp $ # # Config file and device description for machine-independent FDT code. # Included by ports that need it. @@ -669,6 +669,11 @@ device qcaoss attach qcaoss at fdt file dev/fdt/qcaoss.c qcaoss +# Qualcomm CPU Clock +device qccpu +attach qccpu at fdt +file dev/fdt/qccpu.c qccpu + device qcdwusb: fdt attach qcdwusb at fdt file dev/fdt/qcdwusb.c qcdwusb diff --git a/sys/dev/fdt/qccpu.c b/sys/dev/fdt/qccpu.c new file mode 100644 index 00000000000..3b562b3c4f8 --- /dev/null +++ b/sys/dev/fdt/qccpu.c @@ -0,0 +1,254 @@ +/* $OpenBSD: qccpu.c,v 1.1 2023/07/01 16:34:30 drahn Exp $ */ +/* + * Copyright (c) 2023 Dale Rahn + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include +#include + +#include +#include +#include + +#include +#include +#include + +#define CPUF_ENABLE 0x000 +#define CPUF_DOMAIN_STATE 0x020 +#define CPUF_DOMAIN_STATE_LVAL_M 0xff +#define CPUF_DOMAIN_STATE_LVAL_S 0 +#define CPUF_DVCS_CTRL 0x0b0 +#define CPUF_DVCS_CTRL_PER_CORE 0x1 +#define CPUF_FREQ_LUT 0x100 +#define CPUF_FREQ_LUT_SRC_M 0x1 +#define CPUF_FREQ_LUT_SRC_S 30 +#define CPUF_FREQ_LUT_CORES_M 0x7 +#define CPUF_FREQ_LUT_CORES_S 16 +#define CPUF_FREQ_LUT_LVAL_M 0xff +#define CPUF_FREQ_LUT_LVAL_S 0 +#define CPUF_VOLT_LUT 0x200 +#define CPUF_VOLT_LUT_IDX_M 0x2f +#define CPUF_VOLT_LUT_IDX_S 16 +#define CPUF_VOLT_LUT_VOLT_M 0xfff +#define CPUF_VOLT_LUT_VOLT_S 0 +#define CPUF_PERF_STATE 0x320 +#define LUT_ROW_SIZE 4 + +struct cpu_freq_tbl { + uint32_t driver_data; + uint32_t frequency; +}; + +#define NUM_GROUP 2 +#define MAX_LUT 40 + +#define XO_FREQ_HZ 19200000 + +struct qccpu_softc { + struct device sc_dev; + bus_space_tag_t sc_iot; + bus_space_handle_t sc_ioh[2]; + + int sc_node; + + struct clock_device sc_cd; + uint32_t sc_freq[NUM_GROUP][MAX_LUT]; + int sc_num_lut[NUM_GROUP]; +}; + +#define DEVNAME(sc) (sc)->sc_dev.dv_xname + +int qccpu_match(struct device *, void *, void *); +void qccpu_attach(struct device *, struct device *, void *); +void qccpu_enable(void *, uint32_t *, int); +int qccpu_set_frequency(void *, uint32_t *, uint32_t); +uint32_t qccpu_get_frequency(void *, uint32_t *); +uint32_t qccpu_lut_to_freq(struct qccpu_softc *, int, uint32_t); +uint32_t qccpu_lut_to_cores(struct qccpu_softc *, int, uint32_t); + +void qccpu_collect_lut(struct qccpu_softc *sc, int); + + +const struct cfattach qccpu_ca = { + sizeof (struct qccpu_softc), qccpu_match, qccpu_attach +}; + +struct cfdriver qccpu_cd = { + NULL, "qccpu", DV_DULL +}; + +int +qccpu_match(struct device *parent, void *match, void *aux) +{ + struct fdt_attach_args *faa = aux; + + return OF_is_compatible(faa->fa_node, "qcom,cpufreq-epss"); +} + +void +qccpu_attach(struct device *parent, struct device *self, void *aux) +{ + struct qccpu_softc *sc = (struct qccpu_softc *)self; + struct fdt_attach_args *faa = aux; + + if (faa->fa_nreg < 2) { + printf(": no registers\n"); + return; + } + + sc->sc_iot = faa->fa_iot; + if (bus_space_map(sc->sc_iot, faa->fa_reg[0].addr, + faa->fa_reg[0].size, 0, &sc->sc_ioh[0])) { + printf(": can't map registers (cluster0)\n"); + return; + } + + if (bus_space_map(sc->sc_iot, faa->fa_reg[1].addr, + faa->fa_reg[1].size, 0, &sc->sc_ioh[1])) { + printf(": can't map registers (cluster1)\n"); + return; + } + sc->sc_node = faa->fa_node; + + printf("\n"); + + qccpu_collect_lut(sc, 0); + qccpu_collect_lut(sc, 1); + + sc->sc_cd.cd_node = faa->fa_node; + sc->sc_cd.cd_cookie = sc; + sc->sc_cd.cd_get_frequency = qccpu_get_frequency; + sc->sc_cd.cd_set_frequency = qccpu_set_frequency; + clock_register(&sc->sc_cd); +} + +void +qccpu_collect_lut(struct qccpu_softc *sc, int group) +{ + int prev_freq = 0; + uint32_t freq; + int idx; + bus_space_tag_t iot = sc->sc_iot; + bus_space_handle_t ioh = sc->sc_ioh[group]; + + for (idx = 0; ; idx++) { + freq = bus_space_read_4(iot, ioh, + CPUF_FREQ_LUT + idx * LUT_ROW_SIZE); + + if (idx != 0 && prev_freq == freq) { + sc->sc_num_lut[group] = idx; + break; + } + + sc->sc_freq[group][idx] = freq; + +#ifdef DEBUG + printf("%s: %d: %x %u\n", DEVNAME(sc), idx, freq, + qccpu_lut_to_freq(sc, idx, group)); +#endif /* DEBUG */ + + prev_freq = freq; + if (idx >= MAX_LUT-1) + break; + } + + return; +} + +uint32_t +qccpu_get_frequency(void *cookie, uint32_t *cells) +{ + struct qccpu_softc *sc = cookie; + bus_space_tag_t iot = sc->sc_iot; + bus_space_handle_t ioh; + uint32_t lval; + uint32_t group; + + if (cells[0] >= 2) { + printf("%s: bad cell %d\n", __func__, cells[0]); + return 0; + } + group = cells[0]; + + ioh = sc->sc_ioh[cells[0]]; + + lval = (bus_space_read_4(iot, ioh, CPUF_DOMAIN_STATE) + >> CPUF_DOMAIN_STATE_LVAL_S) & CPUF_DOMAIN_STATE_LVAL_M; + return lval *XO_FREQ_HZ; +} + +int +qccpu_set_frequency(void *cookie, uint32_t *cells, uint32_t freq) +{ + struct qccpu_softc *sc = cookie; + bus_space_tag_t iot = sc->sc_iot; + bus_space_handle_t ioh; + int index = 0; + int numcores, i; + uint32_t group; + + if (cells[0] >= 2) { + printf("%s: bad cell %d\n", __func__, cells[0]); + return 1; + } + group = cells[0]; + + ioh = sc->sc_ioh[group]; + + while (index < sc->sc_num_lut[group]) { + if (freq == qccpu_lut_to_freq(sc, index, group)) + break; + + if (freq < qccpu_lut_to_freq(sc, index, group)) { + /* select next slower if not match, not zero */ + if (index != 0) + index = index - 1; + break; + } + + index++; + } + +#ifdef DEBUG + printf("%s called freq %u index %d\n", __func__, freq, index); +#endif /* DEBUG */ + + if ((bus_space_read_4(iot, ioh, CPUF_DVCS_CTRL) & + CPUF_DVCS_CTRL_PER_CORE) != 0) + numcores = qccpu_lut_to_cores(sc, index, group); + else + numcores = 1; + for (i = 0; i < numcores; i++) + bus_space_write_4(iot, ioh, CPUF_PERF_STATE + i * 4, index); + + return 0; +} + +uint32_t +qccpu_lut_to_freq(struct qccpu_softc *sc, int index, uint32_t group) +{ + return XO_FREQ_HZ * + ((sc->sc_freq[group][index] >> CPUF_FREQ_LUT_LVAL_S) + & CPUF_FREQ_LUT_LVAL_M); +} + +uint32_t +qccpu_lut_to_cores(struct qccpu_softc *sc, int index, uint32_t group) +{ + return ((sc->sc_freq[group][index] >> CPUF_FREQ_LUT_CORES_S) + & CPUF_FREQ_LUT_CORES_M); +} -- 2.20.1