1/*
2 * dump_psb. (c) 2004, Dave Jones, Red Hat Inc.
3 * Licensed under the GPL v2.
4 */
5
6#include <fcntl.h>
7#include <stdio.h>
8#include <stdlib.h>
9#include <string.h>
10#include <unistd.h>
11
12#define _GNU_SOURCE
13#include <getopt.h>
14
15#include <sys/mman.h>
16
17#define LEN (0x100000 - 0xc0000)
18#define OFFSET (0xc0000)
19
20#ifndef __packed
21#define __packed __attribute((packed))
22#endif
23
24static long relevant;
25
26static const int fid_to_mult[32] = {
27	110, 115, 120, 125, 50, 55, 60, 65,
28	70, 75, 80, 85, 90, 95, 100, 105,
29	30, 190, 40, 200, 130, 135, 140, 210,
30	150, 225, 160, 165, 170, 180, -1, -1,
31};
32
33static const int vid_to_voltage[32] = {
34	2000, 1950, 1900, 1850, 1800, 1750, 1700, 1650,
35	1600, 1550, 1500, 1450, 1400, 1350, 1300, 0,
36	1275, 1250, 1225, 1200, 1175, 1150, 1125, 1100,
37	1075, 1050, 1024, 1000, 975, 950, 925, 0,
38};
39
40struct psb_header {
41	char signature[10];
42	u_char version;
43	u_char flags;
44	u_short settlingtime;
45	u_char res1;
46	u_char numpst;
47} __packed;
48
49struct pst_header {
50	u_int32_t cpuid;
51	u_char fsb;
52	u_char maxfid;
53	u_char startvid;
54	u_char numpstates;
55} __packed;
56
57static u_int fsb;
58static u_int sgtc;
59
60static int
61decode_pst(char *p, int npstates)
62{
63	int i;
64	int freq, fid, vid;
65
66	for (i = 0; i < npstates; ++i) {
67		fid = *p++;
68		vid = *p++;
69		freq = 100 * fid_to_mult[fid] * fsb;
70
71		printf("   %2d %8dkHz  FID %02x (%2d.%01d)  VID %02x (%4dmV)\n",
72		       i,
73		       freq,
74		       fid, fid_to_mult[fid]/10, fid_to_mult[fid]%10,
75		       vid, vid_to_voltage[vid]);
76	}
77
78	return 0;
79}
80
81static
82void decode_psb(char *p, int numpst)
83{
84	int i;
85	struct psb_header *psb;
86	struct pst_header *pst;
87
88	psb = (struct psb_header*) p;
89
90	if (psb->version != 0x12)
91		return;
92
93	printf("PSB version: %hhx flags: %hhx settling time %hhuus res1 %hhx num pst %hhu\n",
94			psb->version,
95			psb->flags,
96			psb->settlingtime,
97			psb->res1,
98			psb->numpst);
99	sgtc = psb->settlingtime * 100;
100
101	if (sgtc < 10000)
102		sgtc = 10000;
103
104	p = ((char *) psb) + sizeof(struct psb_header);
105
106	if (numpst < 0)
107		numpst = psb->numpst;
108	else
109		printf("Overriding number of pst :%d\n", numpst);
110
111	for (i = 0; i < numpst; i++) {
112		pst = (struct pst_header*) p;
113
114		if (relevant != 0) {
115			if (relevant!= pst->cpuid)
116				goto next_one;
117		}
118
119		printf("  PST %d  cpuid %.3x fsb %hhu mfid %hhx svid %hhx numberstates %hhu\n",
120				i+1,
121				pst->cpuid,
122				pst->fsb,
123				pst->maxfid,
124				pst->startvid,
125				pst->numpstates);
126
127		fsb = pst->fsb;
128		decode_pst(p + sizeof(struct pst_header), pst->numpstates);
129
130next_one:
131		p += sizeof(struct pst_header) + 2*pst->numpstates;
132	}
133
134}
135
136static struct option info_opts[] = {
137	{.name = "numpst",	.has_arg=no_argument,	.flag=NULL, .val='n'},
138};
139
140void print_help(void)
141{
142	printf ("Usage: dump_psb [options]\n");
143	printf ("Options:\n");
144	printf ("  -n, --numpst     Set number of PST tables to scan\n");
145	printf ("  -r, --relevant   Only display PSTs relevant to cpuid N\n");
146}
147
148int
149main(int argc, char *argv[])
150{
151	int fd;
152	int numpst=-1;
153	int ret=0, cont=1;
154	char *mem = NULL;
155	char *p;
156
157	do {
158		ret = getopt_long(argc, argv, "hr:n:", info_opts, NULL);
159		switch (ret){
160		case '?':
161		case 'h':
162			print_help();
163			cont = 0;
164			break;
165		case 'r':
166			relevant = strtol(optarg, NULL, 16);
167			break;
168		case 'n':
169			numpst = strtol(optarg, NULL, 10);
170			break;
171		case -1:
172			cont = 0;
173			break;
174		}
175
176	} while(cont);
177
178	fd = open("/dev/mem", O_RDONLY);
179	if (fd < 0) {
180		printf ("Couldn't open /dev/mem. Are you root?\n");
181		exit(1);
182	}
183
184	mem = mmap(mem, 0x100000 - 0xc0000, PROT_READ, MAP_SHARED, fd, 0xc0000);
185	close(fd);
186
187	for (p = mem; p - mem < LEN; p+=16) {
188		if (memcmp(p, "AMDK7PNOW!", 10) == 0) {
189			decode_psb(p, numpst);
190			break;
191		}
192	}
193
194	munmap(mem, LEN);
195	return 0;
196}
197