1/*
2 * Copyright 2009, Haiku, Inc. All Rights Reserved.
3 * Distributed under the terms of the MIT License.
4 *
5 * Authors:
6 *		Clemens Zeidler, haiku@clemens-zeidler.de
7 */
8
9
10#include "frequency.h"
11
12#include <arch/x86/arch_cpu.h>
13
14#include <stdio.h>
15#include <stdlib.h>
16#include <string.h>
17
18
19void
20est_get_id16(uint16* _id16)
21{
22	*_id16 = x86_read_msr(MSR_GET_FREQ_STATE) & 0xffff;
23}
24
25
26status_t
27est_set_id16(uint16 id16, bool needCheck)
28{
29	uint64 msr;
30
31	// Read the current register, mask out the old, set the new id.
32	msr = x86_read_msr(MSR_GET_FREQ_STATE);
33	msr = (msr & ~0xffff) | id16;
34	x86_write_msr(MSR_SET_FREQ_STATE, msr);
35
36	if (needCheck) {
37		// Wait a short while for the new setting.  XXX Is this necessary?
38		snooze(EST_TRANS_LAT);
39
40		uint16 newID16;
41		est_get_id16(&newID16);
42		if (newID16 != id16)
43			return B_ERROR;
44		TRACE("EST: set frequency ok, id %i\n", id16);
45	}
46	return B_OK;
47}
48
49
50freq_info*
51est_get_current(freq_info* list)
52{
53	// Try a few times to get a valid value.  Sometimes, if the CPU
54	// is in the middle of an asynchronous transition (i.e., P4TCC),
55	// we get a temporary invalid result.
56	for (uint32 i = 0; i < 5; i++) {
57		uint16 id16;
58		est_get_id16(&id16);
59
60		for (freq_info* info = list; info->id != 0; info++) {
61			if (info->id == id16)
62				return info;
63		}
64
65		snooze(100);
66	}
67	return NULL;
68}
69
70
71status_t
72est_get_info(freq_info** _frequencyInfos)
73{
74	uint64 msr = x86_read_msr(MSR_GET_FREQ_STATE);
75	status_t status = est_table_info(msr, _frequencyInfos);
76	if (status != B_OK) {
77		TRACE("EST: Get frequency table from model specific register\n");
78		status = est_msr_info(msr, _frequencyInfos);
79	}
80
81	if (status != B_OK) {
82		TRACE("est: CPU supports Enhanced Speedstep, but is not recognized.\n");
83		return status;
84	}
85
86	return B_OK;
87}
88
89
90status_t
91est_table_info(uint64 msr, freq_info** _frequencyInfos)
92{
93	// Find a table which matches (vendor, id32).
94	system_info info;
95	if (get_system_info(&info) != B_OK)
96		return B_ERROR;
97
98	ss_cpu_info* cpuInfo;
99	uint32 id = msr >> 32;
100	for (cpuInfo = ESTprocs; cpuInfo->id32 != 0; cpuInfo++) {
101		if (cpuInfo->vendor_id == uint32(info.cpu_type & B_CPU_x86_VENDOR_MASK)
102			&& cpuInfo->id32 == id)
103			break;
104	}
105	if (cpuInfo->id32 == 0)
106		return B_ERROR;
107
108	// Make sure the current setpoint is valid.
109	if (est_get_current(cpuInfo->freqtab) == NULL) {
110		TRACE("current setting not found in table\n");
111		return B_ERROR;
112	}
113
114	*_frequencyInfos = cpuInfo->freqtab;
115	return B_OK;
116}
117
118
119bool
120bus_speed_ok(int bus)
121{
122	switch (bus) {
123		case 100:
124		case 133:
125		case 166:
126		case 333:
127			return true;
128		default:
129			return false;
130	}
131}
132
133
134/*!	Flesh out a simple rate table containing the high and low frequencies
135	based on the current clock speed and the upper 32 bits of the MSR.
136*/
137status_t
138est_msr_info(uint64 msr, freq_info** _frequencyInfos)
139{
140	// Figure out the bus clock.
141	system_info info;
142	if (get_system_info(&info) != B_OK)
143		return B_ERROR;
144
145	int32 freq = info.cpu_clock_speed / 1000000;
146	uint16 id = msr >> 32;
147	int32 bus = 0;
148	if (id >> 8)
149		bus = freq / (id >> 8);
150
151	TRACE("est: Guessed bus clock (high) of %d MHz\n", int(bus));
152	if (!bus_speed_ok(bus)) {
153		// We may be running on the low frequency.
154		id = msr >> 48;
155		if (id >> 8)
156			bus = freq / (id >> 8);
157		TRACE("est: Guessed bus clock (low) of %d MHz\n", int(bus));
158		if (!bus_speed_ok(bus))
159			return B_ERROR;
160
161		// Calculate high frequency.
162		id = msr >> 32;
163		freq = ((id >> 8) & 0xff) * bus;
164	}
165
166	// Fill out a new freq table containing just the high and low freqs.
167	freq_info* frequencyInfo = (freq_info*)malloc(sizeof(freq_info) * 3);
168	if (frequencyInfo == NULL)
169		return B_NO_MEMORY;
170
171	memset(frequencyInfo, 0, sizeof(freq_info) * 3);
172
173	// First, the high frequency.
174	int32 volts = id & 0xff;
175	if (volts != 0) {
176		volts <<= 4;
177		volts += 700;
178	}
179	frequencyInfo[0].frequency = freq;
180	frequencyInfo[0].volts = volts;
181	frequencyInfo[0].id = id;
182	frequencyInfo[0].power = CPUFREQ_VAL_UNKNOWN;
183	TRACE("Guessed high setting of %d MHz @ %d Mv\n", int(freq), int(volts));
184
185	// Second, the low frequency.
186	id = msr >> 48;
187	freq = ((id >> 8) & 0xff) * bus;
188	volts = id & 0xff;
189	if (volts != 0) {
190		volts <<= 4;
191		volts += 700;
192	}
193	frequencyInfo[1].frequency = freq;
194	frequencyInfo[1].volts = volts;
195	frequencyInfo[1].id = id;
196	frequencyInfo[1].power = CPUFREQ_VAL_UNKNOWN;
197	TRACE("Guessed low setting of %d MHz @ %d Mv\n", int(freq), int(volts));
198
199	// Table is already terminated due to M_ZERO.
200	*_frequencyInfos = frequencyInfo;
201	return B_OK;
202}
203