1c39f472eSBen Skeggs /*
2c39f472eSBen Skeggs  * Copyright 2012 Nouveau Community
3c39f472eSBen Skeggs  *
4c39f472eSBen Skeggs  * Permission is hereby granted, free of charge, to any person obtaining a
5c39f472eSBen Skeggs  * copy of this software and associated documentation files (the "Software"),
6c39f472eSBen Skeggs  * to deal in the Software without restriction, including without limitation
7c39f472eSBen Skeggs  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8c39f472eSBen Skeggs  * and/or sell copies of the Software, and to permit persons to whom the
9c39f472eSBen Skeggs  * Software is furnished to do so, subject to the following conditions:
10c39f472eSBen Skeggs  *
11c39f472eSBen Skeggs  * The above copyright notice and this permission notice shall be included in
12c39f472eSBen Skeggs  * all copies or substantial portions of the Software.
13c39f472eSBen Skeggs  *
14c39f472eSBen Skeggs  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15c39f472eSBen Skeggs  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16c39f472eSBen Skeggs  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
17c39f472eSBen Skeggs  * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
18c39f472eSBen Skeggs  * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
19c39f472eSBen Skeggs  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
20c39f472eSBen Skeggs  * OTHER DEALINGS IN THE SOFTWARE.
21c39f472eSBen Skeggs  *
22c39f472eSBen Skeggs  * Authors: Martin Peres
23c39f472eSBen Skeggs  */
24c39f472eSBen Skeggs #include <subdev/bios.h>
25c39f472eSBen Skeggs #include <subdev/bios/bit.h>
26c39f472eSBen Skeggs #include <subdev/bios/therm.h>
27c39f472eSBen Skeggs 
28a215721fSBen Skeggs static u32
therm_table(struct nvkm_bios * bios,u8 * ver,u8 * hdr,u8 * len,u8 * cnt)29d390b480SBen Skeggs therm_table(struct nvkm_bios *bios, u8 *ver, u8 *hdr, u8 *len, u8 *cnt)
30c39f472eSBen Skeggs {
31c39f472eSBen Skeggs 	struct bit_entry bit_P;
32a215721fSBen Skeggs 	u32 therm = 0;
33c39f472eSBen Skeggs 
34c39f472eSBen Skeggs 	if (!bit_entry(bios, 'P', &bit_P)) {
35c39f472eSBen Skeggs 		if (bit_P.version == 1)
36a215721fSBen Skeggs 			therm = nvbios_rd32(bios, bit_P.offset + 12);
37c39f472eSBen Skeggs 		else if (bit_P.version == 2)
38a215721fSBen Skeggs 			therm = nvbios_rd32(bios, bit_P.offset + 16);
39c39f472eSBen Skeggs 		else
4060b29d20SBen Skeggs 			nvkm_error(&bios->subdev,
41c39f472eSBen Skeggs 				   "unknown offset for thermal in BIT P %d\n",
42c39f472eSBen Skeggs 				   bit_P.version);
43c39f472eSBen Skeggs 	}
44c39f472eSBen Skeggs 
45c39f472eSBen Skeggs 	/* exit now if we haven't found the thermal table */
46c39f472eSBen Skeggs 	if (!therm)
47a215721fSBen Skeggs 		return 0;
48c39f472eSBen Skeggs 
497f5f518fSBen Skeggs 	*ver = nvbios_rd08(bios, therm + 0);
507f5f518fSBen Skeggs 	*hdr = nvbios_rd08(bios, therm + 1);
517f5f518fSBen Skeggs 	*len = nvbios_rd08(bios, therm + 2);
527f5f518fSBen Skeggs 	*cnt = nvbios_rd08(bios, therm + 3);
537f5f518fSBen Skeggs 	return therm + nvbios_rd08(bios, therm + 1);
54c39f472eSBen Skeggs }
55c39f472eSBen Skeggs 
56a215721fSBen Skeggs static u32
nvbios_therm_entry(struct nvkm_bios * bios,int idx,u8 * ver,u8 * len)57d390b480SBen Skeggs nvbios_therm_entry(struct nvkm_bios *bios, int idx, u8 *ver, u8 *len)
58c39f472eSBen Skeggs {
59c39f472eSBen Skeggs 	u8 hdr, cnt;
60a215721fSBen Skeggs 	u32 therm = therm_table(bios, ver, &hdr, len, &cnt);
61c39f472eSBen Skeggs 	if (therm && idx < cnt)
62c39f472eSBen Skeggs 		return therm + idx * *len;
63a215721fSBen Skeggs 	return 0;
64c39f472eSBen Skeggs }
65c39f472eSBen Skeggs 
66c39f472eSBen Skeggs int
nvbios_therm_sensor_parse(struct nvkm_bios * bios,enum nvbios_therm_domain domain,struct nvbios_therm_sensor * sensor)67d390b480SBen Skeggs nvbios_therm_sensor_parse(struct nvkm_bios *bios,
68c39f472eSBen Skeggs 			  enum nvbios_therm_domain domain,
69c39f472eSBen Skeggs 			  struct nvbios_therm_sensor *sensor)
70c39f472eSBen Skeggs {
71c39f472eSBen Skeggs 	s8 thrs_section, sensor_section, offset;
72c39f472eSBen Skeggs 	u8 ver, len, i;
73a215721fSBen Skeggs 	u32 entry;
74c39f472eSBen Skeggs 
75c39f472eSBen Skeggs 	/* we only support the core domain for now */
76c39f472eSBen Skeggs 	if (domain != NVBIOS_THERM_DOMAIN_CORE)
77c39f472eSBen Skeggs 		return -EINVAL;
78c39f472eSBen Skeggs 
79c39f472eSBen Skeggs 	/* Read the entries from the table */
80c39f472eSBen Skeggs 	thrs_section = 0;
81c39f472eSBen Skeggs 	sensor_section = -1;
82c39f472eSBen Skeggs 	i = 0;
83c39f472eSBen Skeggs 	while ((entry = nvbios_therm_entry(bios, i++, &ver, &len))) {
847f5f518fSBen Skeggs 		s16 value = nvbios_rd16(bios, entry + 1);
85c39f472eSBen Skeggs 
867f5f518fSBen Skeggs 		switch (nvbios_rd08(bios, entry + 0)) {
87c39f472eSBen Skeggs 		case 0x0:
88c39f472eSBen Skeggs 			thrs_section = value;
89c39f472eSBen Skeggs 			if (value > 0)
90c39f472eSBen Skeggs 				return 0; /* we do not try to support ambient */
91c39f472eSBen Skeggs 			break;
92c39f472eSBen Skeggs 		case 0x01:
93c39f472eSBen Skeggs 			sensor_section++;
94c39f472eSBen Skeggs 			if (sensor_section == 0) {
957f5f518fSBen Skeggs 				offset = ((s8) nvbios_rd08(bios, entry + 2)) / 2;
96c39f472eSBen Skeggs 				sensor->offset_constant = offset;
97c39f472eSBen Skeggs 			}
98c39f472eSBen Skeggs 			break;
99c39f472eSBen Skeggs 
100c39f472eSBen Skeggs 		case 0x04:
101c39f472eSBen Skeggs 			if (thrs_section == 0) {
102c39f472eSBen Skeggs 				sensor->thrs_critical.temp = (value & 0xff0) >> 4;
103c39f472eSBen Skeggs 				sensor->thrs_critical.hysteresis = value & 0xf;
104c39f472eSBen Skeggs 			}
105c39f472eSBen Skeggs 			break;
106c39f472eSBen Skeggs 
107c39f472eSBen Skeggs 		case 0x07:
108c39f472eSBen Skeggs 			if (thrs_section == 0) {
109c39f472eSBen Skeggs 				sensor->thrs_down_clock.temp = (value & 0xff0) >> 4;
110c39f472eSBen Skeggs 				sensor->thrs_down_clock.hysteresis = value & 0xf;
111c39f472eSBen Skeggs 			}
112c39f472eSBen Skeggs 			break;
113c39f472eSBen Skeggs 
114c39f472eSBen Skeggs 		case 0x08:
115c39f472eSBen Skeggs 			if (thrs_section == 0) {
116c39f472eSBen Skeggs 				sensor->thrs_fan_boost.temp = (value & 0xff0) >> 4;
117c39f472eSBen Skeggs 				sensor->thrs_fan_boost.hysteresis = value & 0xf;
118c39f472eSBen Skeggs 			}
119c39f472eSBen Skeggs 			break;
120c39f472eSBen Skeggs 
121c39f472eSBen Skeggs 		case 0x10:
122c39f472eSBen Skeggs 			if (sensor_section == 0)
123c39f472eSBen Skeggs 				sensor->offset_num = value;
124c39f472eSBen Skeggs 			break;
125c39f472eSBen Skeggs 
126c39f472eSBen Skeggs 		case 0x11:
127c39f472eSBen Skeggs 			if (sensor_section == 0)
128c39f472eSBen Skeggs 				sensor->offset_den = value;
129c39f472eSBen Skeggs 			break;
130c39f472eSBen Skeggs 
131c39f472eSBen Skeggs 		case 0x12:
132c39f472eSBen Skeggs 			if (sensor_section == 0)
133c39f472eSBen Skeggs 				sensor->slope_mult = value;
134c39f472eSBen Skeggs 			break;
135c39f472eSBen Skeggs 
136c39f472eSBen Skeggs 		case 0x13:
137c39f472eSBen Skeggs 			if (sensor_section == 0)
138c39f472eSBen Skeggs 				sensor->slope_div = value;
139c39f472eSBen Skeggs 			break;
140c39f472eSBen Skeggs 		case 0x32:
141c39f472eSBen Skeggs 			if (thrs_section == 0) {
142c39f472eSBen Skeggs 				sensor->thrs_shutdown.temp = (value & 0xff0) >> 4;
143c39f472eSBen Skeggs 				sensor->thrs_shutdown.hysteresis = value & 0xf;
144c39f472eSBen Skeggs 			}
145c39f472eSBen Skeggs 			break;
146c39f472eSBen Skeggs 		}
147c39f472eSBen Skeggs 	}
148c39f472eSBen Skeggs 
149c39f472eSBen Skeggs 	return 0;
150c39f472eSBen Skeggs }
151c39f472eSBen Skeggs 
152c39f472eSBen Skeggs int
nvbios_therm_fan_parse(struct nvkm_bios * bios,struct nvbios_therm_fan * fan)153d390b480SBen Skeggs nvbios_therm_fan_parse(struct nvkm_bios *bios, struct nvbios_therm_fan *fan)
154c39f472eSBen Skeggs {
155d390b480SBen Skeggs 	struct nvbios_therm_trip_point *cur_trip = NULL;
156c39f472eSBen Skeggs 	u8 ver, len, i;
157a215721fSBen Skeggs 	u32 entry;
158c39f472eSBen Skeggs 
159c39f472eSBen Skeggs 	uint8_t duty_lut[] = { 0, 0, 25, 0, 40, 0, 50, 0,
160c39f472eSBen Skeggs 				75, 0, 85, 0, 100, 0, 100, 0 };
161c39f472eSBen Skeggs 
162c39f472eSBen Skeggs 	i = 0;
163c39f472eSBen Skeggs 	fan->nr_fan_trip = 0;
164c39f472eSBen Skeggs 	fan->fan_mode = NVBIOS_THERM_FAN_OTHER;
165c39f472eSBen Skeggs 	while ((entry = nvbios_therm_entry(bios, i++, &ver, &len))) {
1667f5f518fSBen Skeggs 		s16 value = nvbios_rd16(bios, entry + 1);
167c39f472eSBen Skeggs 
1687f5f518fSBen Skeggs 		switch (nvbios_rd08(bios, entry + 0)) {
169c39f472eSBen Skeggs 		case 0x22:
170c39f472eSBen Skeggs 			fan->min_duty = value & 0xff;
171c39f472eSBen Skeggs 			fan->max_duty = (value & 0xff00) >> 8;
172c39f472eSBen Skeggs 			break;
173c39f472eSBen Skeggs 		case 0x24:
174c39f472eSBen Skeggs 			fan->nr_fan_trip++;
175c39f472eSBen Skeggs 			if (fan->fan_mode > NVBIOS_THERM_FAN_TRIP)
176c39f472eSBen Skeggs 				fan->fan_mode = NVBIOS_THERM_FAN_TRIP;
177c39f472eSBen Skeggs 			cur_trip = &fan->trip[fan->nr_fan_trip - 1];
178c39f472eSBen Skeggs 			cur_trip->hysteresis = value & 0xf;
179c39f472eSBen Skeggs 			cur_trip->temp = (value & 0xff0) >> 4;
180c39f472eSBen Skeggs 			cur_trip->fan_duty = duty_lut[(value & 0xf000) >> 12];
181c39f472eSBen Skeggs 			break;
182c39f472eSBen Skeggs 		case 0x25:
183c39f472eSBen Skeggs 			cur_trip = &fan->trip[fan->nr_fan_trip - 1];
184c39f472eSBen Skeggs 			cur_trip->fan_duty = value;
185c39f472eSBen Skeggs 			break;
186c39f472eSBen Skeggs 		case 0x26:
187c39f472eSBen Skeggs 			if (!fan->pwm_freq)
188c39f472eSBen Skeggs 				fan->pwm_freq = value;
189c39f472eSBen Skeggs 			break;
190c39f472eSBen Skeggs 		case 0x3b:
191c39f472eSBen Skeggs 			fan->bump_period = value;
192c39f472eSBen Skeggs 			break;
193c39f472eSBen Skeggs 		case 0x3c:
194c39f472eSBen Skeggs 			fan->slow_down_period = value;
195c39f472eSBen Skeggs 			break;
196c39f472eSBen Skeggs 		case 0x46:
197c39f472eSBen Skeggs 			if (fan->fan_mode > NVBIOS_THERM_FAN_LINEAR)
198c39f472eSBen Skeggs 				fan->fan_mode = NVBIOS_THERM_FAN_LINEAR;
1997f5f518fSBen Skeggs 			fan->linear_min_temp = nvbios_rd08(bios, entry + 1);
2007f5f518fSBen Skeggs 			fan->linear_max_temp = nvbios_rd08(bios, entry + 2);
201c39f472eSBen Skeggs 			break;
202c39f472eSBen Skeggs 		}
203c39f472eSBen Skeggs 	}
204c39f472eSBen Skeggs 
205c39f472eSBen Skeggs 	/* starting from fermi, fan management is always linear */
20646484438SBen Skeggs 	if (bios->subdev.device->card_type >= NV_C0 &&
207c39f472eSBen Skeggs 		fan->fan_mode == NVBIOS_THERM_FAN_OTHER) {
208c39f472eSBen Skeggs 		fan->fan_mode = NVBIOS_THERM_FAN_LINEAR;
209c39f472eSBen Skeggs 	}
210c39f472eSBen Skeggs 
211c39f472eSBen Skeggs 	return 0;
212c39f472eSBen Skeggs }
213