xref: /netbsd-src/sys/external/bsd/drm2/dist/drm/nouveau/nvkm/subdev/therm/nouveau_nvkm_subdev_therm_g84.c (revision 41ec02673d281bbb3d38e6c78504ce6e30c228c1)
1 /*	$NetBSD: nouveau_nvkm_subdev_therm_g84.c,v 1.3 2021/12/18 23:45:41 riastradh Exp $	*/
2 
3 /*
4  * Copyright 2012 Red Hat Inc.
5  *
6  * Permission is hereby granted, free of charge, to any person obtaining a
7  * copy of this software and associated documentation files (the "Software"),
8  * to deal in the Software without restriction, including without limitation
9  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
10  * and/or sell copies of the Software, and to permit persons to whom the
11  * Software is furnished to do so, subject to the following conditions:
12  *
13  * The above copyright notice and this permission notice shall be included in
14  * all copies or substantial portions of the Software.
15  *
16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
19  * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
20  * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
21  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
22  * OTHER DEALINGS IN THE SOFTWARE.
23  *
24  * Authors: Ben Skeggs
25  * 	    Martin Peres
26  */
27 #include <sys/cdefs.h>
28 __KERNEL_RCSID(0, "$NetBSD: nouveau_nvkm_subdev_therm_g84.c,v 1.3 2021/12/18 23:45:41 riastradh Exp $");
29 
30 #include "priv.h"
31 
32 #include <subdev/fuse.h>
33 
34 int
g84_temp_get(struct nvkm_therm * therm)35 g84_temp_get(struct nvkm_therm *therm)
36 {
37 	struct nvkm_device *device = therm->subdev.device;
38 
39 	if (nvkm_fuse_read(device->fuse, 0x1a8) == 1)
40 		return nvkm_rd32(device, 0x20400);
41 	else
42 		return -ENODEV;
43 }
44 
45 void
g84_sensor_setup(struct nvkm_therm * therm)46 g84_sensor_setup(struct nvkm_therm *therm)
47 {
48 	struct nvkm_device *device = therm->subdev.device;
49 
50 	/* enable temperature reading for cards with insane defaults */
51 	if (nvkm_fuse_read(device->fuse, 0x1a8) == 1) {
52 		nvkm_mask(device, 0x20008, 0x80008000, 0x80000000);
53 		nvkm_mask(device, 0x2000c, 0x80000003, 0x00000000);
54 		mdelay(20); /* wait for the temperature to stabilize */
55 	}
56 }
57 
58 static void
g84_therm_program_alarms(struct nvkm_therm * therm)59 g84_therm_program_alarms(struct nvkm_therm *therm)
60 {
61 	struct nvbios_therm_sensor *sensor = &therm->bios_sensor;
62 	struct nvkm_subdev *subdev = &therm->subdev;
63 	struct nvkm_device *device = subdev->device;
64 	unsigned long flags;
65 
66 	spin_lock_irqsave(&therm->sensor.alarm_program_lock, flags);
67 
68 	/* enable RISING and FALLING IRQs for shutdown, THRS 0, 1, 2 and 4 */
69 	nvkm_wr32(device, 0x20000, 0x000003ff);
70 
71 	/* shutdown: The computer should be shutdown when reached */
72 	nvkm_wr32(device, 0x20484, sensor->thrs_shutdown.hysteresis);
73 	nvkm_wr32(device, 0x20480, sensor->thrs_shutdown.temp);
74 
75 	/* THRS_1 : fan boost*/
76 	nvkm_wr32(device, 0x204c4, sensor->thrs_fan_boost.temp);
77 
78 	/* THRS_2 : critical */
79 	nvkm_wr32(device, 0x204c0, sensor->thrs_critical.temp);
80 
81 	/* THRS_4 : down clock */
82 	nvkm_wr32(device, 0x20414, sensor->thrs_down_clock.temp);
83 	spin_unlock_irqrestore(&therm->sensor.alarm_program_lock, flags);
84 
85 	nvkm_debug(subdev,
86 		   "Programmed thresholds [ %d(%d), %d(%d), %d(%d), %d(%d) ]\n",
87 		   sensor->thrs_fan_boost.temp,
88 		   sensor->thrs_fan_boost.hysteresis,
89 		   sensor->thrs_down_clock.temp,
90 		   sensor->thrs_down_clock.hysteresis,
91 		   sensor->thrs_critical.temp,
92 		   sensor->thrs_critical.hysteresis,
93 		   sensor->thrs_shutdown.temp,
94 		   sensor->thrs_shutdown.hysteresis);
95 
96 }
97 
98 /* must be called with alarm_program_lock taken ! */
99 static void
g84_therm_threshold_hyst_emulation(struct nvkm_therm * therm,uint32_t thrs_reg,u8 status_bit,const struct nvbios_therm_threshold * thrs,enum nvkm_therm_thrs thrs_name)100 g84_therm_threshold_hyst_emulation(struct nvkm_therm *therm,
101 				   uint32_t thrs_reg, u8 status_bit,
102 				   const struct nvbios_therm_threshold *thrs,
103 				   enum nvkm_therm_thrs thrs_name)
104 {
105 	struct nvkm_device *device = therm->subdev.device;
106 	enum nvkm_therm_thrs_direction direction;
107 	enum nvkm_therm_thrs_state prev_state, new_state;
108 	int temp, cur;
109 
110 	prev_state = nvkm_therm_sensor_get_threshold_state(therm, thrs_name);
111 	temp = nvkm_rd32(device, thrs_reg);
112 
113 	/* program the next threshold */
114 	if (temp == thrs->temp) {
115 		nvkm_wr32(device, thrs_reg, thrs->temp - thrs->hysteresis);
116 		new_state = NVKM_THERM_THRS_HIGHER;
117 	} else {
118 		nvkm_wr32(device, thrs_reg, thrs->temp);
119 		new_state = NVKM_THERM_THRS_LOWER;
120 	}
121 
122 	/* fix the state (in case someone reprogrammed the alarms) */
123 	cur = therm->func->temp_get(therm);
124 	if (new_state == NVKM_THERM_THRS_LOWER && cur > thrs->temp)
125 		new_state = NVKM_THERM_THRS_HIGHER;
126 	else if (new_state == NVKM_THERM_THRS_HIGHER &&
127 		cur < thrs->temp - thrs->hysteresis)
128 		new_state = NVKM_THERM_THRS_LOWER;
129 	nvkm_therm_sensor_set_threshold_state(therm, thrs_name, new_state);
130 
131 	/* find the direction */
132 	if (prev_state < new_state)
133 		direction = NVKM_THERM_THRS_RISING;
134 	else if (prev_state > new_state)
135 		direction = NVKM_THERM_THRS_FALLING;
136 	else
137 		return;
138 
139 	/* advertise a change in direction */
140 	nvkm_therm_sensor_event(therm, thrs_name, direction);
141 }
142 
143 static void
g84_therm_intr(struct nvkm_therm * therm)144 g84_therm_intr(struct nvkm_therm *therm)
145 {
146 	struct nvkm_subdev *subdev = &therm->subdev;
147 	struct nvkm_device *device = subdev->device;
148 	struct nvbios_therm_sensor *sensor = &therm->bios_sensor;
149 	unsigned long flags;
150 	uint32_t intr;
151 
152 	spin_lock_irqsave(&therm->sensor.alarm_program_lock, flags);
153 
154 	intr = nvkm_rd32(device, 0x20100) & 0x3ff;
155 
156 	/* THRS_4: downclock */
157 	if (intr & 0x002) {
158 		g84_therm_threshold_hyst_emulation(therm, 0x20414, 24,
159 						   &sensor->thrs_down_clock,
160 						   NVKM_THERM_THRS_DOWNCLOCK);
161 		intr &= ~0x002;
162 	}
163 
164 	/* shutdown */
165 	if (intr & 0x004) {
166 		g84_therm_threshold_hyst_emulation(therm, 0x20480, 20,
167 						   &sensor->thrs_shutdown,
168 						   NVKM_THERM_THRS_SHUTDOWN);
169 		intr &= ~0x004;
170 	}
171 
172 	/* THRS_1 : fan boost */
173 	if (intr & 0x008) {
174 		g84_therm_threshold_hyst_emulation(therm, 0x204c4, 21,
175 						   &sensor->thrs_fan_boost,
176 						   NVKM_THERM_THRS_FANBOOST);
177 		intr &= ~0x008;
178 	}
179 
180 	/* THRS_2 : critical */
181 	if (intr & 0x010) {
182 		g84_therm_threshold_hyst_emulation(therm, 0x204c0, 22,
183 						   &sensor->thrs_critical,
184 						   NVKM_THERM_THRS_CRITICAL);
185 		intr &= ~0x010;
186 	}
187 
188 	if (intr)
189 		nvkm_error(subdev, "intr %08x\n", intr);
190 
191 	/* ACK everything */
192 	nvkm_wr32(device, 0x20100, 0xffffffff);
193 	nvkm_wr32(device, 0x1100, 0x10000); /* PBUS */
194 
195 	spin_unlock_irqrestore(&therm->sensor.alarm_program_lock, flags);
196 }
197 
198 void
g84_therm_fini(struct nvkm_therm * therm)199 g84_therm_fini(struct nvkm_therm *therm)
200 {
201 	struct nvkm_device *device = therm->subdev.device;
202 
203 	/* Disable PTherm IRQs */
204 	nvkm_wr32(device, 0x20000, 0x00000000);
205 
206 	/* ACK all PTherm IRQs */
207 	nvkm_wr32(device, 0x20100, 0xffffffff);
208 	nvkm_wr32(device, 0x1100, 0x10000); /* PBUS */
209 }
210 
211 void
g84_therm_init(struct nvkm_therm * therm)212 g84_therm_init(struct nvkm_therm *therm)
213 {
214 	g84_sensor_setup(therm);
215 }
216 
217 static const struct nvkm_therm_func
218 g84_therm = {
219 	.init = g84_therm_init,
220 	.fini = g84_therm_fini,
221 	.intr = g84_therm_intr,
222 	.pwm_ctrl = nv50_fan_pwm_ctrl,
223 	.pwm_get = nv50_fan_pwm_get,
224 	.pwm_set = nv50_fan_pwm_set,
225 	.pwm_clock = nv50_fan_pwm_clock,
226 	.temp_get = g84_temp_get,
227 	.program_alarms = g84_therm_program_alarms,
228 };
229 
230 int
g84_therm_new(struct nvkm_device * device,int index,struct nvkm_therm ** ptherm)231 g84_therm_new(struct nvkm_device *device, int index, struct nvkm_therm **ptherm)
232 {
233 	struct nvkm_therm *therm;
234 	int ret;
235 
236 	ret = nvkm_therm_new_(&g84_therm, device, index, &therm);
237 	*ptherm = therm;
238 	if (ret)
239 		return ret;
240 
241 	/* init the thresholds */
242 	nvkm_therm_sensor_set_threshold_state(therm, NVKM_THERM_THRS_SHUTDOWN,
243 						     NVKM_THERM_THRS_LOWER);
244 	nvkm_therm_sensor_set_threshold_state(therm, NVKM_THERM_THRS_FANBOOST,
245 						     NVKM_THERM_THRS_LOWER);
246 	nvkm_therm_sensor_set_threshold_state(therm, NVKM_THERM_THRS_CRITICAL,
247 						     NVKM_THERM_THRS_LOWER);
248 	nvkm_therm_sensor_set_threshold_state(therm, NVKM_THERM_THRS_DOWNCLOCK,
249 						     NVKM_THERM_THRS_LOWER);
250 	return 0;
251 }
252