162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci *  (C) 2004 Bruno Ducrot <ducrot@poupinou.org>
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Based on code found in
662306a36Sopenharmony_ci * linux/arch/i386/kernel/cpu/cpufreq/powernow-k8.c
762306a36Sopenharmony_ci * and originally developed by Paul Devriendt
862306a36Sopenharmony_ci */
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ci#include <stdio.h>
1162306a36Sopenharmony_ci#include <stdlib.h>
1262306a36Sopenharmony_ci#include <stdint.h>
1362306a36Sopenharmony_ci#include <unistd.h>
1462306a36Sopenharmony_ci#include <errno.h>
1562306a36Sopenharmony_ci#include <fcntl.h>
1662306a36Sopenharmony_ci
1762306a36Sopenharmony_ci#include <sys/types.h>
1862306a36Sopenharmony_ci#include <sys/stat.h>
1962306a36Sopenharmony_ci
2062306a36Sopenharmony_ci#define MCPU 32
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_ci#define MSR_FIDVID_STATUS	0xc0010042
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_ci#define MSR_S_HI_CURRENT_VID	0x0000001f
2562306a36Sopenharmony_ci#define MSR_S_LO_CURRENT_FID	0x0000003f
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_cistatic int get_fidvid(uint32_t cpu, uint32_t *fid, uint32_t *vid)
2862306a36Sopenharmony_ci{
2962306a36Sopenharmony_ci	int err = 1;
3062306a36Sopenharmony_ci	uint64_t msr = 0;
3162306a36Sopenharmony_ci	int fd;
3262306a36Sopenharmony_ci	char file[20];
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_ci	if (cpu > MCPU)
3562306a36Sopenharmony_ci		goto out;
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_ci	sprintf(file, "/dev/cpu/%d/msr", cpu);
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_ci	fd = open(file, O_RDONLY);
4062306a36Sopenharmony_ci	if (fd < 0)
4162306a36Sopenharmony_ci		goto out;
4262306a36Sopenharmony_ci	lseek(fd, MSR_FIDVID_STATUS, SEEK_CUR);
4362306a36Sopenharmony_ci	if (read(fd, &msr, 8) != 8)
4462306a36Sopenharmony_ci		goto err1;
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_ci	*fid = ((uint32_t )(msr & 0xffffffffull)) & MSR_S_LO_CURRENT_FID;
4762306a36Sopenharmony_ci	*vid = ((uint32_t )(msr>>32 & 0xffffffffull)) & MSR_S_HI_CURRENT_VID;
4862306a36Sopenharmony_ci	err = 0;
4962306a36Sopenharmony_cierr1:
5062306a36Sopenharmony_ci	close(fd);
5162306a36Sopenharmony_ciout:
5262306a36Sopenharmony_ci	return err;
5362306a36Sopenharmony_ci}
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_ci/* Return a frequency in MHz, given an input fid */
5762306a36Sopenharmony_cistatic uint32_t find_freq_from_fid(uint32_t fid)
5862306a36Sopenharmony_ci{
5962306a36Sopenharmony_ci	return 800 + (fid * 100);
6062306a36Sopenharmony_ci}
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_ci/* Return a voltage in miliVolts, given an input vid */
6362306a36Sopenharmony_cistatic uint32_t find_millivolts_from_vid(uint32_t vid)
6462306a36Sopenharmony_ci{
6562306a36Sopenharmony_ci	return 1550-vid*25;
6662306a36Sopenharmony_ci}
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_ciint main (int argc, char *argv[])
6962306a36Sopenharmony_ci{
7062306a36Sopenharmony_ci	int err;
7162306a36Sopenharmony_ci	int cpu;
7262306a36Sopenharmony_ci	uint32_t fid, vid;
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_ci	if (argc < 2)
7562306a36Sopenharmony_ci		cpu = 0;
7662306a36Sopenharmony_ci	else
7762306a36Sopenharmony_ci		cpu = strtoul(argv[1], NULL, 0);
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_ci	err = get_fidvid(cpu, &fid, &vid);
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_ci	if (err) {
8262306a36Sopenharmony_ci		printf("can't get fid, vid from MSR\n");
8362306a36Sopenharmony_ci		printf("Possible trouble: you don't run a powernow-k8 capable cpu\n");
8462306a36Sopenharmony_ci		printf("or you are not root, or the msr driver is not present\n");
8562306a36Sopenharmony_ci		exit(1);
8662306a36Sopenharmony_ci	}
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_ci	printf("cpu %d currently at %d MHz and %d mV\n",
9062306a36Sopenharmony_ci			cpu,
9162306a36Sopenharmony_ci			find_freq_from_fid(fid),
9262306a36Sopenharmony_ci			find_millivolts_from_vid(vid));
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_ci	return 0;
9562306a36Sopenharmony_ci}
96