Path: blob/main/sys/powerpc/cpufreq/mpc85xx_jog.c
107556 views
/*-1* Copyright (c) 2017 Justin Hibbits2* All rights reserved.3*4* Redistribution and use in source and binary forms, with or without5* modification, are permitted provided that the following conditions6* are met:7* 1. Redistributions of source code must retain the above copyright8* notice, this list of conditions and the following disclaimer.9* 2. Redistributions in binary form must reproduce the above copyright10* notice, this list of conditions and the following disclaimer in the11* documentation and/or other materials provided with the distribution.12*13* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR14* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES15* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.16* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,17* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,18* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;19* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED20* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,21* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY22* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF23* SUCH DAMAGE.24*/2526#include <sys/param.h>27#include <sys/systm.h>28#include <sys/bus.h>29#include <sys/cpu.h>30#include <sys/kernel.h>31#include <sys/module.h>32#include <sys/smp.h>3334#include <dev/ofw/ofw_bus.h>35#include <dev/ofw/ofw_bus_subr.h>3637#include <machine/cpu.h>3839#include <powerpc/mpc85xx/mpc85xx.h>4041#include "cpufreq_if.h"4243/* No worries about uint32_t math overflow in here, because the highest44* multiplier supported is 4, and the highest speed part is still well below45* 2GHz.46*/4748#define GUTS_PORPLLSR (CCSRBAR_VA + 0xe0000)49#define GUTS_PMJCR (CCSRBAR_VA + 0xe007c)50#define PMJCR_RATIO_M 0x3f51#define PMJCR_CORE_MULT(x,y) ((x) << (16 + ((y) * 8)))52#define PMJCR_GET_CORE_MULT(x,y) (((x) >> (16 + ((y) * 8))) & 0x3f)53#define GUTS_POWMGTCSR (CCSRBAR_VA + 0xe0080)54#define POWMGTCSR_JOG 0x0020000055#define POWMGTCSR_INT_MASK 0x00000f005657#define MHZ 10000005859struct mpc85xx_jog_softc {60device_t dev;61int cpu;62int low;63int high;64int min_freq;65};6667static struct ofw_compat_data *mpc85xx_jog_devcompat(void);68static void mpc85xx_jog_identify(driver_t *driver, device_t parent);69static int mpc85xx_jog_probe(device_t dev);70static int mpc85xx_jog_attach(device_t dev);71static int mpc85xx_jog_settings(device_t dev, struct cf_setting *sets, int *count);72static int mpc85xx_jog_set(device_t dev, const struct cf_setting *set);73static int mpc85xx_jog_get(device_t dev, struct cf_setting *set);74static int mpc85xx_jog_type(device_t dev, int *type);7576static device_method_t mpc85xx_jog_methods[] = {77/* Device interface */78DEVMETHOD(device_identify, mpc85xx_jog_identify),79DEVMETHOD(device_probe, mpc85xx_jog_probe),80DEVMETHOD(device_attach, mpc85xx_jog_attach),8182/* cpufreq interface */83DEVMETHOD(cpufreq_drv_set, mpc85xx_jog_set),84DEVMETHOD(cpufreq_drv_get, mpc85xx_jog_get),85DEVMETHOD(cpufreq_drv_type, mpc85xx_jog_type),86DEVMETHOD(cpufreq_drv_settings, mpc85xx_jog_settings),87{0, 0}88};8990static driver_t mpc85xx_jog_driver = {91"jog",92mpc85xx_jog_methods,93sizeof(struct mpc85xx_jog_softc)94};9596DRIVER_MODULE(mpc85xx_jog, cpu, mpc85xx_jog_driver, 0, 0);9798struct mpc85xx_constraints {99int threshold; /* Threshold frequency, in MHz, for setting CORE_SPD bit. */100int min_mult; /* Minimum PLL multiplier. */101};102103static struct mpc85xx_constraints mpc8536_constraints = {104800,1053106};107108static struct mpc85xx_constraints p1022_constraints = {109500,1102111};112113static struct ofw_compat_data jog_compat[] = {114{"fsl,mpc8536-guts", (uintptr_t)&mpc8536_constraints},115{"fsl,p1022-guts", (uintptr_t)&p1022_constraints},116{NULL, 0}117};118119static struct ofw_compat_data *120mpc85xx_jog_devcompat(void)121{122phandle_t node;123int i;124125node = OF_finddevice("/soc");126if (node == -1)127return (NULL);128129for (i = 0; jog_compat[i].ocd_str != NULL; i++)130if (ofw_bus_find_compatible(node, jog_compat[i].ocd_str) > 0)131break;132133if (jog_compat[i].ocd_str == NULL)134return (NULL);135136return (&jog_compat[i]);137}138139static void140mpc85xx_jog_identify(driver_t *driver, device_t parent)141{142struct ofw_compat_data *compat;143144/* Make sure we're not being doubly invoked. */145if (device_find_child(parent, "mpc85xx_jog", DEVICE_UNIT_ANY) != NULL)146return;147148compat = mpc85xx_jog_devcompat();149if (compat == NULL)150return;151152/*153* We attach a child for every CPU since settings need to154* be performed on every CPU in the SMP case.155*/156if (BUS_ADD_CHILD(parent, 10, "jog", DEVICE_UNIT_ANY) == NULL)157device_printf(parent, "add jog child failed\n");158}159160static int161mpc85xx_jog_probe(device_t dev)162{163struct ofw_compat_data *compat;164165compat = mpc85xx_jog_devcompat();166if (compat == NULL || compat->ocd_str == NULL)167return (ENXIO);168169device_set_desc(dev, "Freescale CPU Jogger");170return (0);171}172173static int174mpc85xx_jog_attach(device_t dev)175{176struct ofw_compat_data *compat;177struct mpc85xx_jog_softc *sc;178struct mpc85xx_constraints *constraints;179phandle_t cpu;180uint32_t reg;181182sc = device_get_softc(dev);183sc->dev = dev;184185compat = mpc85xx_jog_devcompat();186constraints = (struct mpc85xx_constraints *)compat->ocd_data;187cpu = ofw_bus_get_node(device_get_parent(dev));188189if (cpu <= 0) {190device_printf(dev,"No CPU device tree node!\n");191return (ENXIO);192}193194OF_getencprop(cpu, "reg", &sc->cpu, sizeof(sc->cpu));195196reg = ccsr_read4(GUTS_PORPLLSR);197198/*199* Assume power-on PLL is the highest PLL config supported on the200* board.201*/202sc->high = PMJCR_GET_CORE_MULT(reg, sc->cpu);203sc->min_freq = constraints->threshold;204sc->low = constraints->min_mult;205206cpufreq_register(dev);207return (0);208}209210static int211mpc85xx_jog_settings(device_t dev, struct cf_setting *sets, int *count)212{213struct mpc85xx_jog_softc *sc;214uint32_t sysclk;215int i;216217sc = device_get_softc(dev);218if (sets == NULL || count == NULL)219return (EINVAL);220if (*count < sc->high - 1)221return (E2BIG);222223sysclk = mpc85xx_get_system_clock();224/* Return a list of valid settings for this driver. */225memset(sets, CPUFREQ_VAL_UNKNOWN, sizeof(*sets) * sc->high);226227for (i = sc->high; i >= sc->low; --i) {228sets[sc->high - i].freq = sysclk * i / MHZ;229sets[sc->high - i].dev = dev;230sets[sc->high - i].spec[0] = i;231}232*count = sc->high - sc->low + 1;233234return (0);235}236237struct jog_rv_args {238int cpu;239int mult;240int slow;241volatile int inprogress;242};243244static void245mpc85xx_jog_set_int(void *arg)246{247struct jog_rv_args *args = arg;248uint32_t reg;249250if (PCPU_GET(cpuid) == args->cpu) {251reg = ccsr_read4(GUTS_PMJCR);252reg &= ~PMJCR_CORE_MULT(PMJCR_RATIO_M, args->cpu);253reg |= PMJCR_CORE_MULT(args->mult, args->cpu);254if (args->slow)255reg &= ~(1 << (12 + args->cpu));256else257reg |= (1 << (12 + args->cpu));258259ccsr_write4(GUTS_PMJCR, reg);260261reg = ccsr_read4(GUTS_POWMGTCSR);262reg |= POWMGTCSR_JOG | POWMGTCSR_INT_MASK;263ccsr_write4(GUTS_POWMGTCSR, reg);264265/* Wait for completion */266do {267DELAY(100);268reg = ccsr_read4(GUTS_POWMGTCSR);269} while (reg & POWMGTCSR_JOG);270271reg = ccsr_read4(GUTS_POWMGTCSR);272ccsr_write4(GUTS_POWMGTCSR, reg & ~POWMGTCSR_INT_MASK);273ccsr_read4(GUTS_POWMGTCSR);274275args->inprogress = 0;276} else {277while (args->inprogress)278cpu_spinwait();279}280}281282static int283mpc85xx_jog_set(device_t dev, const struct cf_setting *set)284{285struct mpc85xx_jog_softc *sc;286struct jog_rv_args args;287288if (set == NULL)289return (EINVAL);290291sc = device_get_softc(dev);292293args.slow = (set->freq <= sc->min_freq);294args.mult = set->spec[0];295args.cpu = PCPU_GET(cpuid);296args.inprogress = 1;297smp_rendezvous(smp_no_rendezvous_barrier, mpc85xx_jog_set_int,298smp_no_rendezvous_barrier, &args);299300return (0);301}302303static int304mpc85xx_jog_get(device_t dev, struct cf_setting *set)305{306struct mpc85xx_jog_softc *sc;307uint32_t pmjcr;308uint32_t freq;309310if (set == NULL)311return (EINVAL);312313sc = device_get_softc(dev);314memset(set, CPUFREQ_VAL_UNKNOWN, sizeof(*set));315316pmjcr = ccsr_read4(GUTS_PORPLLSR);317freq = PMJCR_GET_CORE_MULT(pmjcr, sc->cpu);318freq *= mpc85xx_get_system_clock();319freq /= MHZ;320321set->freq = freq;322set->dev = dev;323324return (0);325}326327static int328mpc85xx_jog_type(device_t dev, int *type)329{330331if (type == NULL)332return (EINVAL);333334*type = CPUFREQ_TYPE_ABSOLUTE;335return (0);336}337338339