Module Name: src Committed By: jmcneill Date: Mon Jan 7 22:32:24 UTC 2013
Modified Files: src/sys/arch/evbarm/rpi: rpi_vcmbox.c vcprop.h Log Message: Add support for rpi cpu frequency scaling through machdep.cpu.frequency.* sysctls. The minimum and maximum supported frequencies are based on the "arm_freq_min" and "arm_freq" values in config.txt. To generate a diff of this commit: cvs rdiff -u -r1.1 -r1.2 src/sys/arch/evbarm/rpi/rpi_vcmbox.c cvs rdiff -u -r1.3 -r1.4 src/sys/arch/evbarm/rpi/vcprop.h Please note that diffs are not public domain; they are subject to the copyright notices on the relevant files.
Modified files: Index: src/sys/arch/evbarm/rpi/rpi_vcmbox.c diff -u src/sys/arch/evbarm/rpi/rpi_vcmbox.c:1.1 src/sys/arch/evbarm/rpi/rpi_vcmbox.c:1.2 --- src/sys/arch/evbarm/rpi/rpi_vcmbox.c:1.1 Mon Jan 7 20:19:33 2013 +++ src/sys/arch/evbarm/rpi/rpi_vcmbox.c Mon Jan 7 22:32:24 2013 @@ -1,4 +1,4 @@ -/* $NetBSD: rpi_vcmbox.c,v 1.1 2013/01/07 20:19:33 jmcneill Exp $ */ +/* $NetBSD: rpi_vcmbox.c,v 1.2 2013/01/07 22:32:24 jmcneill Exp $ */ /*- * Copyright (c) 2013 Jared D. McNeill <jmcne...@invisible.ca> @@ -31,7 +31,7 @@ */ #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: rpi_vcmbox.c,v 1.1 2013/01/07 20:19:33 jmcneill Exp $"); +__KERNEL_RCSID(0, "$NetBSD: rpi_vcmbox.c,v 1.2 2013/01/07 22:32:24 jmcneill Exp $"); #include <sys/param.h> #include <sys/types.h> @@ -40,6 +40,7 @@ __KERNEL_RCSID(0, "$NetBSD: rpi_vcmbox.c #include <sys/conf.h> #include <sys/bus.h> #include <sys/kmem.h> +#include <sys/sysctl.h> #include <dev/sysmon/sysmonvar.h> @@ -54,6 +55,15 @@ struct vcmbox_temp_request { struct vcprop_tag end; } __packed; +struct vcmbox_clockrate_request { + struct vcprop_buffer_hdr vb_hdr; + struct vcprop_tag_clockrate vbt_clockrate; + struct vcprop_tag end; +} __packed; + +#define RATE2MHZ(rate) ((rate) / 1000000) +#define MHZ2RATE(mhz) ((mhz) * 1000000) + #define VCMBOX_INIT_REQUEST(req) \ do { \ memset(&(req), 0, sizeof((req))); \ @@ -76,6 +86,15 @@ struct vcmbox_softc { #define VCMBOX_SENSOR_TEMP 0 #define VCMBOX_NSENSORS 1 envsys_data_t sc_sensor[VCMBOX_NSENSORS]; + + /* cpu frequency scaling */ + struct sysctllog *sc_log; + uint32_t sc_cpu_minrate; + uint32_t sc_cpu_maxrate; + int sc_node_target; + int sc_node_current; + int sc_node_min; + int sc_node_max; }; static const char *vcmbox_sensor_name[VCMBOX_NSENSORS] = { @@ -89,9 +108,17 @@ static int vcmbox_sensor_id[VCMBOX_NSENS static int vcmbox_match(device_t, cfdata_t, void *); static void vcmbox_attach(device_t, device_t, void *); -static void vcmbox_create_sensors(struct vcmbox_softc *); static int vcmbox_read_temp(struct vcmbox_softc *, uint32_t, int, uint32_t *); +static int vcmbox_read_clockrate(struct vcmbox_softc *, uint32_t, int, + uint32_t *); +static int vcmbox_write_clockrate(struct vcmbox_softc *, uint32_t, int, + uint32_t); + +static int vcmbox_cpufreq_init(struct vcmbox_softc *); +static int vcmbox_cpufreq_sysctl_helper(SYSCTLFN_PROTO); + +static void vcmbox_create_sensors(struct vcmbox_softc *); static void vcmbox_sensor_get_limits(struct sysmon_envsys *, envsys_data_t *, sysmon_envsys_lim_t *, uint32_t *); @@ -117,6 +144,8 @@ vcmbox_attach(device_t parent, device_t aprint_naive("\n"); aprint_normal("\n"); + vcmbox_cpufreq_init(sc); + sc->sc_sme = sysmon_envsys_create(); sc->sc_sme->sme_cookie = sc; sc->sc_sme->sme_name = device_xname(sc->sc_dev); @@ -126,28 +155,6 @@ vcmbox_attach(device_t parent, device_t sysmon_envsys_register(sc->sc_sme); } -static void -vcmbox_create_sensors(struct vcmbox_softc *sc) -{ - uint32_t val; - - sc->sc_sensor[VCMBOX_SENSOR_TEMP].sensor = VCMBOX_SENSOR_TEMP; - sc->sc_sensor[VCMBOX_SENSOR_TEMP].units = ENVSYS_STEMP; - sc->sc_sensor[VCMBOX_SENSOR_TEMP].state = ENVSYS_SINVALID; - sc->sc_sensor[VCMBOX_SENSOR_TEMP].flags = ENVSYS_FHAS_ENTROPY; - strlcpy(sc->sc_sensor[VCMBOX_SENSOR_TEMP].desc, - vcmbox_sensor_name[VCMBOX_SENSOR_TEMP], - sizeof(sc->sc_sensor[VCMBOX_SENSOR_TEMP].desc)); - if (vcmbox_read_temp(sc, VCPROPTAG_GET_MAX_TEMPERATURE, - vcmbox_sensor_id[VCMBOX_SENSOR_TEMP], &val) == 0) { - sc->sc_sensor[VCMBOX_SENSOR_TEMP].value_max = - val * 1000 + 273150000; - sc->sc_sensor[VCMBOX_SENSOR_TEMP].flags |= ENVSYS_FVALID_MAX; - } - sysmon_envsys_sensor_attach(sc->sc_sme, - &sc->sc_sensor[VCMBOX_SENSOR_TEMP]); -} - static int vcmbox_read_temp(struct vcmbox_softc *sc, uint32_t tag, int id, uint32_t *val) { @@ -170,6 +177,198 @@ vcmbox_read_temp(struct vcmbox_softc *sc return 0; } +static int +vcmbox_read_clockrate(struct vcmbox_softc *sc, uint32_t tag, int id, + uint32_t *val) +{ + struct vcmbox_clockrate_request vb; + uint32_t res; + int error; + + VCMBOX_INIT_REQUEST(vb); + VCMBOX_INIT_TAG(vb.vbt_clockrate, tag); + vb.vbt_clockrate.id = id; + error = bcmmbox_request(BCMMBOX_CHANARM2VC, &vb, sizeof(vb), &res); + if (error) + return error; + if (!vcprop_buffer_success_p(&vb.vb_hdr) || + !vcprop_tag_success_p(&vb.vbt_clockrate.tag)) { + return EIO; + } + *val = vb.vbt_clockrate.rate; + + return 0; +} + +static int +vcmbox_write_clockrate(struct vcmbox_softc *sc, uint32_t tag, int id, + uint32_t val) +{ + struct vcmbox_clockrate_request vb; + uint32_t res; + int error; + + VCMBOX_INIT_REQUEST(vb); + VCMBOX_INIT_TAG(vb.vbt_clockrate, tag); + vb.vbt_clockrate.id = id; + vb.vbt_clockrate.rate = val; + error = bcmmbox_request(BCMMBOX_CHANARM2VC, &vb, sizeof(vb), &res); + if (error) + return error; + if (!vcprop_buffer_success_p(&vb.vb_hdr) || + !vcprop_tag_success_p(&vb.vbt_clockrate.tag)) { + return EIO; + } + + return 0; +} + + +static int +vcmbox_cpufreq_init(struct vcmbox_softc *sc) +{ + const struct sysctlnode *node, *cpunode, *freqnode; + int error; + + error = vcmbox_read_clockrate(sc, VCPROPTAG_GET_MIN_CLOCKRATE, + VCPROP_CLK_ARM, &sc->sc_cpu_minrate); + if (error) { + aprint_error_dev(sc->sc_dev, "couldn't read min clkrate (%d)\n", + error); + return error; + } + error = vcmbox_read_clockrate(sc, VCPROPTAG_GET_MAX_CLOCKRATE, + VCPROP_CLK_ARM, &sc->sc_cpu_maxrate); + if (error) { + aprint_error_dev(sc->sc_dev, "couldn't read max clkrate (%d)\n", + error); + return error; + } + + error = sysctl_createv(&sc->sc_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(&sc->sc_log, 0, &node, &cpunode, + 0, CTLTYPE_NODE, "cpu", NULL, + NULL, 0, NULL, 0, CTL_CREATE, CTL_EOL); + if (error) + goto sysctl_failed; + error = sysctl_createv(&sc->sc_log, 0, &cpunode, &freqnode, + 0, CTLTYPE_NODE, "frequency", NULL, + NULL, 0, NULL, 0, CTL_CREATE, CTL_EOL); + if (error) + goto sysctl_failed; + + error = sysctl_createv(&sc->sc_log, 0, &freqnode, &node, + CTLFLAG_READWRITE, CTLTYPE_INT, "target", NULL, + vcmbox_cpufreq_sysctl_helper, 0, (void *)sc, 0, + CTL_CREATE, CTL_EOL); + if (error) + goto sysctl_failed; + sc->sc_node_target = node->sysctl_num; + + error = sysctl_createv(&sc->sc_log, 0, &freqnode, &node, + 0, CTLTYPE_INT, "current", NULL, + vcmbox_cpufreq_sysctl_helper, 0, (void *)sc, 0, + CTL_CREATE, CTL_EOL); + if (error) + goto sysctl_failed; + sc->sc_node_current = node->sysctl_num; + + error = sysctl_createv(&sc->sc_log, 0, &freqnode, &node, + 0, CTLTYPE_INT, "min", NULL, + vcmbox_cpufreq_sysctl_helper, 0, (void *)sc, 0, + CTL_CREATE, CTL_EOL); + if (error) + goto sysctl_failed; + sc->sc_node_min = node->sysctl_num; + + error = sysctl_createv(&sc->sc_log, 0, &freqnode, &node, + 0, CTLTYPE_INT, "max", NULL, + vcmbox_cpufreq_sysctl_helper, 0, (void *)sc, 0, + CTL_CREATE, CTL_EOL); + if (error) + goto sysctl_failed; + sc->sc_node_max = node->sysctl_num; + + return 0; + +sysctl_failed: + aprint_error_dev(sc->sc_dev, "couldn't create sysctl nodes (%d)\n", + error); + sysctl_teardown(&sc->sc_log); + return error; +} + +static int +vcmbox_cpufreq_sysctl_helper(SYSCTLFN_ARGS) +{ + struct sysctlnode node; + struct vcmbox_softc *sc; + int fq, oldfq = 0, error; + uint32_t rate; + + node = *rnode; + sc = node.sysctl_data; + + node.sysctl_data = &fq; + + if (rnode->sysctl_num == sc->sc_node_target || + rnode->sysctl_num == sc->sc_node_current) { + error = vcmbox_read_clockrate(sc, VCPROPTAG_GET_CLOCKRATE, + VCPROP_CLK_ARM, &rate); + if (error) + return error; + fq = RATE2MHZ(rate); + if (rnode->sysctl_num == sc->sc_node_target) + oldfq = fq; + } else if (rnode->sysctl_num == sc->sc_node_min) { + fq = RATE2MHZ(sc->sc_cpu_minrate); + } else if (rnode->sysctl_num == sc->sc_node_max) { + fq = RATE2MHZ(sc->sc_cpu_maxrate); + } else + return EOPNOTSUPP; + + error = sysctl_lookup(SYSCTLFN_CALL(&node)); + if (error || newp == NULL) + return error; + + if (fq == oldfq || rnode->sysctl_num != sc->sc_node_target) + return 0; + + if (fq < RATE2MHZ(sc->sc_cpu_minrate)) + fq = RATE2MHZ(sc->sc_cpu_minrate); + if (fq > RATE2MHZ(sc->sc_cpu_maxrate)) + fq = RATE2MHZ(sc->sc_cpu_maxrate); + + return vcmbox_write_clockrate(sc, VCPROPTAG_SET_CLOCKRATE, + VCPROP_CLK_ARM, MHZ2RATE(fq)); +} + +static void +vcmbox_create_sensors(struct vcmbox_softc *sc) +{ + uint32_t val; + + sc->sc_sensor[VCMBOX_SENSOR_TEMP].sensor = VCMBOX_SENSOR_TEMP; + sc->sc_sensor[VCMBOX_SENSOR_TEMP].units = ENVSYS_STEMP; + sc->sc_sensor[VCMBOX_SENSOR_TEMP].state = ENVSYS_SINVALID; + sc->sc_sensor[VCMBOX_SENSOR_TEMP].flags = ENVSYS_FHAS_ENTROPY; + strlcpy(sc->sc_sensor[VCMBOX_SENSOR_TEMP].desc, + vcmbox_sensor_name[VCMBOX_SENSOR_TEMP], + sizeof(sc->sc_sensor[VCMBOX_SENSOR_TEMP].desc)); + if (vcmbox_read_temp(sc, VCPROPTAG_GET_MAX_TEMPERATURE, + vcmbox_sensor_id[VCMBOX_SENSOR_TEMP], &val) == 0) { + sc->sc_sensor[VCMBOX_SENSOR_TEMP].value_max = + val * 1000 + 273150000; + sc->sc_sensor[VCMBOX_SENSOR_TEMP].flags |= ENVSYS_FVALID_MAX; + } + sysmon_envsys_sensor_attach(sc->sc_sme, + &sc->sc_sensor[VCMBOX_SENSOR_TEMP]); +} + static void vcmbox_sensor_get_limits(struct sysmon_envsys *sme, envsys_data_t *edata, sysmon_envsys_lim_t *limits, uint32_t *props) Index: src/sys/arch/evbarm/rpi/vcprop.h diff -u src/sys/arch/evbarm/rpi/vcprop.h:1.3 src/sys/arch/evbarm/rpi/vcprop.h:1.4 --- src/sys/arch/evbarm/rpi/vcprop.h:1.3 Mon Jan 7 20:18:28 2013 +++ src/sys/arch/evbarm/rpi/vcprop.h Mon Jan 7 22:32:24 2013 @@ -1,4 +1,4 @@ -/* $NetBSD: vcprop.h,v 1.3 2013/01/07 20:18:28 jmcneill Exp $ */ +/* $NetBSD: vcprop.h,v 1.4 2013/01/07 22:32:24 jmcneill Exp $ */ /*- * Copyright (c) 2012 The NetBSD Foundation, Inc. @@ -56,6 +56,8 @@ struct vcprop_tag { #define VCPROPTAG_SET_CLOCKSTATE 0x00038001 #define VCPROPTAG_GET_CLOCKRATE 0x00030002 #define VCPROPTAG_SET_CLOCKRATE 0x00038002 +#define VCPROPTAG_GET_MIN_CLOCKRATE 0x00030007 +#define VCPROPTAG_GET_MAX_CLOCKRATE 0x00030004 #define VCPROPTAG_GET_VOLTAGE 0x00030003 #define VCPROPTAG_SET_VOLTAGE 0x00038003