掌機 - Miyoo Mini Plus - C/C++ - Set CPU Clock



main.c

#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/mman.h>

#define BASE_REG_RIU_PA   0x1f000000
#define BASE_REG_MPLL_PA  (BASE_REG_RIU_PA + 0x103000 * 2)
#define PLL_SIZE          0x1000

static void write_file(const char* fname, char* str)
{
    int fd = open(fname, O_WRONLY);

    if (fd >= 0) {
        write(fd, str, strlen(str));
        close(fd);
    }
}

static int set_cpuclock(uint32_t newclock)
{
    int fd_mem = -1;
    void *pll_map = NULL;
    uint32_t post_div = 0;
    char clockstr[16] = {0};
    const char fn_governor[] = "/sys/devices/system/cpu/cpufreq/policy0/scaling_governor";
    const char fn_setspeed[] = "/sys/devices/system/cpu/cpufreq/policy0/scaling_setspeed";
 
    fd_mem = open("/dev/mem", O_RDWR);
    if (fd_mem < 0) {
        return -1;
    }
 
    pll_map = mmap(0, PLL_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd_mem, BASE_REG_MPLL_PA);
    if (pll_map) {
        printf("Set cpuclock %dMHz\n", newclock);
 
        newclock *= 1000;
        sprintf(clockstr, "%d", newclock);
        write_file(fn_governor, "userspace");
        write_file(fn_setspeed, clockstr);
 
        if (newclock >= 800000) {
            post_div = 2;
        }
        else if (newclock >= 400000) {
            post_div = 4;
        }
        else if (newclock >= 200000) {
            post_div = 8;
        }
        else {
            post_div = 16;
        }
 
        {
            static const uint64_t divsrc = 432000000llu * 524288;
            uint32_t rate = (newclock * 1000) / 16 * post_div / 2;
            uint32_t lpf = (uint32_t)(divsrc / rate);
            volatile uint16_t *p16 = (uint16_t *)pll_map;
            uint32_t cur_post_div = (p16[0x232] & 0x0f) + 1;
            uint32_t tmp_post_div = cur_post_div;
 
            if (post_div > cur_post_div) {
                while (tmp_post_div != post_div) {
                    tmp_post_div <<= 1;
                    p16[0x232] = (p16[0x232] & 0xf0) | ((tmp_post_div - 1) & 0x0f);
                }
            }
 
            p16[0x2A8] = 0x0000;            // reg_lpf_enable = 0
            p16[0x2AE] = 0x000f;            // reg_lpf_update_cnt = 32
            p16[0x2A4] = lpf & 0xffff;      // set target freq to LPF high
            p16[0x2A6] = lpf >> 16;         // set target freq to LPF high
            p16[0x2B0] = 0x0001;            // switch to LPF control
            p16[0x2B2]|= 0x1000;            // from low to high
            p16[0x2A8] = 0x0001;            // reg_lpf_enable = 1
            while (!(p16[0x2ba] & 1));      // polling done
            p16[0x2A0] = lpf & 0xffff;      // store freq to LPF low
            p16[0x2A2] = lpf >> 16;         // store freq to LPF low
 
            if (post_div < cur_post_div) {
                while (tmp_post_div != post_div) {
                    tmp_post_div >>= 1;
                    p16[0x232] = (p16[0x232] & 0xf0) | ((tmp_post_div - 1) & 0x0f);
                }
            }
        }
        munmap(pll_map, PLL_SIZE);
    }
    close(fd_mem);
    return 0;
}

int main(int argc, char **argv)
{
    set_cpuclock(1500);
    return 0;
}

P.S. newclock = 1500 = 1.5GHz