xref: /openbmc/linux/drivers/gpu/drm/nouveau/nvkm/subdev/therm/g84.c (revision d0034a7a4ac7fae708146ac0059b9c47a1543f0d)
1e1404611SBen Skeggs /*
2e1404611SBen Skeggs  * Copyright 2012 Red Hat Inc.
3e1404611SBen Skeggs  *
4e1404611SBen Skeggs  * Permission is hereby granted, free of charge, to any person obtaining a
5e1404611SBen Skeggs  * copy of this software and associated documentation files (the "Software"),
6e1404611SBen Skeggs  * to deal in the Software without restriction, including without limitation
7e1404611SBen Skeggs  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8e1404611SBen Skeggs  * and/or sell copies of the Software, and to permit persons to whom the
9e1404611SBen Skeggs  * Software is furnished to do so, subject to the following conditions:
10e1404611SBen Skeggs  *
11e1404611SBen Skeggs  * The above copyright notice and this permission notice shall be included in
12e1404611SBen Skeggs  * all copies or substantial portions of the Software.
13e1404611SBen Skeggs  *
14e1404611SBen Skeggs  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15e1404611SBen Skeggs  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16e1404611SBen Skeggs  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
17e1404611SBen Skeggs  * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
18e1404611SBen Skeggs  * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
19e1404611SBen Skeggs  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
20e1404611SBen Skeggs  * OTHER DEALINGS IN THE SOFTWARE.
21e1404611SBen Skeggs  *
22e1404611SBen Skeggs  * Authors: Ben Skeggs
23e1404611SBen Skeggs  * 	    Martin Peres
24e1404611SBen Skeggs  */
25e1404611SBen Skeggs #include "priv.h"
26e1404611SBen Skeggs 
27e1404611SBen Skeggs #include <subdev/fuse.h>
28e1404611SBen Skeggs 
29e1404611SBen Skeggs int
g84_temp_get(struct nvkm_therm * therm)30e1404611SBen Skeggs g84_temp_get(struct nvkm_therm *therm)
31e1404611SBen Skeggs {
325718ea32SBen Skeggs 	struct nvkm_device *device = therm->subdev.device;
33e1404611SBen Skeggs 
34c5fcafa5SBen Skeggs 	if (nvkm_fuse_read(device->fuse, 0x1a8) == 1)
355718ea32SBen Skeggs 		return nvkm_rd32(device, 0x20400);
36e1404611SBen Skeggs 	else
37e1404611SBen Skeggs 		return -ENODEV;
38e1404611SBen Skeggs }
39e1404611SBen Skeggs 
40e1404611SBen Skeggs void
g84_sensor_setup(struct nvkm_therm * therm)41e1404611SBen Skeggs g84_sensor_setup(struct nvkm_therm *therm)
42e1404611SBen Skeggs {
435718ea32SBen Skeggs 	struct nvkm_device *device = therm->subdev.device;
44e1404611SBen Skeggs 
45e1404611SBen Skeggs 	/* enable temperature reading for cards with insane defaults */
46c5fcafa5SBen Skeggs 	if (nvkm_fuse_read(device->fuse, 0x1a8) == 1) {
475718ea32SBen Skeggs 		nvkm_mask(device, 0x20008, 0x80008000, 0x80000000);
485718ea32SBen Skeggs 		nvkm_mask(device, 0x2000c, 0x80000003, 0x00000000);
49e1404611SBen Skeggs 		mdelay(20); /* wait for the temperature to stabilize */
50e1404611SBen Skeggs 	}
51e1404611SBen Skeggs }
52e1404611SBen Skeggs 
53e1404611SBen Skeggs static void
g84_therm_program_alarms(struct nvkm_therm * therm)5457113c01SBen Skeggs g84_therm_program_alarms(struct nvkm_therm *therm)
55e1404611SBen Skeggs {
56da06b46bSBen Skeggs 	struct nvbios_therm_sensor *sensor = &therm->bios_sensor;
5757113c01SBen Skeggs 	struct nvkm_subdev *subdev = &therm->subdev;
58b3c418bbSBen Skeggs 	struct nvkm_device *device = subdev->device;
59e1404611SBen Skeggs 	unsigned long flags;
60e1404611SBen Skeggs 
61da06b46bSBen Skeggs 	spin_lock_irqsave(&therm->sensor.alarm_program_lock, flags);
62e1404611SBen Skeggs 
63e1404611SBen Skeggs 	/* enable RISING and FALLING IRQs for shutdown, THRS 0, 1, 2 and 4 */
645718ea32SBen Skeggs 	nvkm_wr32(device, 0x20000, 0x000003ff);
65e1404611SBen Skeggs 
66e1404611SBen Skeggs 	/* shutdown: The computer should be shutdown when reached */
675718ea32SBen Skeggs 	nvkm_wr32(device, 0x20484, sensor->thrs_shutdown.hysteresis);
685718ea32SBen Skeggs 	nvkm_wr32(device, 0x20480, sensor->thrs_shutdown.temp);
69e1404611SBen Skeggs 
70e1404611SBen Skeggs 	/* THRS_1 : fan boost*/
715718ea32SBen Skeggs 	nvkm_wr32(device, 0x204c4, sensor->thrs_fan_boost.temp);
72e1404611SBen Skeggs 
73e1404611SBen Skeggs 	/* THRS_2 : critical */
745718ea32SBen Skeggs 	nvkm_wr32(device, 0x204c0, sensor->thrs_critical.temp);
75e1404611SBen Skeggs 
76e1404611SBen Skeggs 	/* THRS_4 : down clock */
775718ea32SBen Skeggs 	nvkm_wr32(device, 0x20414, sensor->thrs_down_clock.temp);
78da06b46bSBen Skeggs 	spin_unlock_irqrestore(&therm->sensor.alarm_program_lock, flags);
79e1404611SBen Skeggs 
80b3c418bbSBen Skeggs 	nvkm_debug(subdev,
81e1404611SBen Skeggs 		   "Programmed thresholds [ %d(%d), %d(%d), %d(%d), %d(%d) ]\n",
82b3c418bbSBen Skeggs 		   sensor->thrs_fan_boost.temp,
83b3c418bbSBen Skeggs 		   sensor->thrs_fan_boost.hysteresis,
84e1404611SBen Skeggs 		   sensor->thrs_down_clock.temp,
85e1404611SBen Skeggs 		   sensor->thrs_down_clock.hysteresis,
86b3c418bbSBen Skeggs 		   sensor->thrs_critical.temp,
87b3c418bbSBen Skeggs 		   sensor->thrs_critical.hysteresis,
88b3c418bbSBen Skeggs 		   sensor->thrs_shutdown.temp,
89b3c418bbSBen Skeggs 		   sensor->thrs_shutdown.hysteresis);
90e1404611SBen Skeggs 
91e1404611SBen Skeggs }
92e1404611SBen Skeggs 
93e1404611SBen Skeggs /* must be called with alarm_program_lock taken ! */
94e1404611SBen Skeggs 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)95e1404611SBen Skeggs g84_therm_threshold_hyst_emulation(struct nvkm_therm *therm,
96e1404611SBen Skeggs 				   uint32_t thrs_reg, u8 status_bit,
97e1404611SBen Skeggs 				   const struct nvbios_therm_threshold *thrs,
98e1404611SBen Skeggs 				   enum nvkm_therm_thrs thrs_name)
99e1404611SBen Skeggs {
1005718ea32SBen Skeggs 	struct nvkm_device *device = therm->subdev.device;
101e1404611SBen Skeggs 	enum nvkm_therm_thrs_direction direction;
102e1404611SBen Skeggs 	enum nvkm_therm_thrs_state prev_state, new_state;
103e1404611SBen Skeggs 	int temp, cur;
104e1404611SBen Skeggs 
105e1404611SBen Skeggs 	prev_state = nvkm_therm_sensor_get_threshold_state(therm, thrs_name);
1065718ea32SBen Skeggs 	temp = nvkm_rd32(device, thrs_reg);
107e1404611SBen Skeggs 
108e1404611SBen Skeggs 	/* program the next threshold */
109e1404611SBen Skeggs 	if (temp == thrs->temp) {
1105718ea32SBen Skeggs 		nvkm_wr32(device, thrs_reg, thrs->temp - thrs->hysteresis);
111e1404611SBen Skeggs 		new_state = NVKM_THERM_THRS_HIGHER;
112e1404611SBen Skeggs 	} else {
1135718ea32SBen Skeggs 		nvkm_wr32(device, thrs_reg, thrs->temp);
114e1404611SBen Skeggs 		new_state = NVKM_THERM_THRS_LOWER;
115e1404611SBen Skeggs 	}
116e1404611SBen Skeggs 
117e1404611SBen Skeggs 	/* fix the state (in case someone reprogrammed the alarms) */
11857113c01SBen Skeggs 	cur = therm->func->temp_get(therm);
119e1404611SBen Skeggs 	if (new_state == NVKM_THERM_THRS_LOWER && cur > thrs->temp)
120e1404611SBen Skeggs 		new_state = NVKM_THERM_THRS_HIGHER;
121e1404611SBen Skeggs 	else if (new_state == NVKM_THERM_THRS_HIGHER &&
122e1404611SBen Skeggs 		cur < thrs->temp - thrs->hysteresis)
123e1404611SBen Skeggs 		new_state = NVKM_THERM_THRS_LOWER;
124e1404611SBen Skeggs 	nvkm_therm_sensor_set_threshold_state(therm, thrs_name, new_state);
125e1404611SBen Skeggs 
126e1404611SBen Skeggs 	/* find the direction */
127e1404611SBen Skeggs 	if (prev_state < new_state)
128e1404611SBen Skeggs 		direction = NVKM_THERM_THRS_RISING;
129e1404611SBen Skeggs 	else if (prev_state > new_state)
130e1404611SBen Skeggs 		direction = NVKM_THERM_THRS_FALLING;
131e1404611SBen Skeggs 	else
132e1404611SBen Skeggs 		return;
133e1404611SBen Skeggs 
134e1404611SBen Skeggs 	/* advertise a change in direction */
135e1404611SBen Skeggs 	nvkm_therm_sensor_event(therm, thrs_name, direction);
136e1404611SBen Skeggs }
137e1404611SBen Skeggs 
138e1404611SBen Skeggs static void
g84_therm_intr(struct nvkm_therm * therm)13957113c01SBen Skeggs g84_therm_intr(struct nvkm_therm *therm)
140e1404611SBen Skeggs {
14157113c01SBen Skeggs 	struct nvkm_subdev *subdev = &therm->subdev;
14257113c01SBen Skeggs 	struct nvkm_device *device = subdev->device;
143da06b46bSBen Skeggs 	struct nvbios_therm_sensor *sensor = &therm->bios_sensor;
144e1404611SBen Skeggs 	unsigned long flags;
145e1404611SBen Skeggs 	uint32_t intr;
146e1404611SBen Skeggs 
147da06b46bSBen Skeggs 	spin_lock_irqsave(&therm->sensor.alarm_program_lock, flags);
148e1404611SBen Skeggs 
1495718ea32SBen Skeggs 	intr = nvkm_rd32(device, 0x20100) & 0x3ff;
150e1404611SBen Skeggs 
151e1404611SBen Skeggs 	/* THRS_4: downclock */
152e1404611SBen Skeggs 	if (intr & 0x002) {
15357113c01SBen Skeggs 		g84_therm_threshold_hyst_emulation(therm, 0x20414, 24,
154e1404611SBen Skeggs 						   &sensor->thrs_down_clock,
155e1404611SBen Skeggs 						   NVKM_THERM_THRS_DOWNCLOCK);
156e1404611SBen Skeggs 		intr &= ~0x002;
157e1404611SBen Skeggs 	}
158e1404611SBen Skeggs 
159e1404611SBen Skeggs 	/* shutdown */
160e1404611SBen Skeggs 	if (intr & 0x004) {
16157113c01SBen Skeggs 		g84_therm_threshold_hyst_emulation(therm, 0x20480, 20,
162e1404611SBen Skeggs 						   &sensor->thrs_shutdown,
163e1404611SBen Skeggs 						   NVKM_THERM_THRS_SHUTDOWN);
164e1404611SBen Skeggs 		intr &= ~0x004;
165e1404611SBen Skeggs 	}
166e1404611SBen Skeggs 
167e1404611SBen Skeggs 	/* THRS_1 : fan boost */
168e1404611SBen Skeggs 	if (intr & 0x008) {
16957113c01SBen Skeggs 		g84_therm_threshold_hyst_emulation(therm, 0x204c4, 21,
170e1404611SBen Skeggs 						   &sensor->thrs_fan_boost,
171e1404611SBen Skeggs 						   NVKM_THERM_THRS_FANBOOST);
172e1404611SBen Skeggs 		intr &= ~0x008;
173e1404611SBen Skeggs 	}
174e1404611SBen Skeggs 
175e1404611SBen Skeggs 	/* THRS_2 : critical */
176e1404611SBen Skeggs 	if (intr & 0x010) {
17757113c01SBen Skeggs 		g84_therm_threshold_hyst_emulation(therm, 0x204c0, 22,
178e1404611SBen Skeggs 						   &sensor->thrs_critical,
179e1404611SBen Skeggs 						   NVKM_THERM_THRS_CRITICAL);
180e1404611SBen Skeggs 		intr &= ~0x010;
181e1404611SBen Skeggs 	}
182e1404611SBen Skeggs 
183e1404611SBen Skeggs 	if (intr)
184b3c418bbSBen Skeggs 		nvkm_error(subdev, "intr %08x\n", intr);
185e1404611SBen Skeggs 
186e1404611SBen Skeggs 	/* ACK everything */
1875718ea32SBen Skeggs 	nvkm_wr32(device, 0x20100, 0xffffffff);
1885718ea32SBen Skeggs 	nvkm_wr32(device, 0x1100, 0x10000); /* PBUS */
189e1404611SBen Skeggs 
190da06b46bSBen Skeggs 	spin_unlock_irqrestore(&therm->sensor.alarm_program_lock, flags);
191e1404611SBen Skeggs }
192e1404611SBen Skeggs 
19357113c01SBen Skeggs void
g84_therm_fini(struct nvkm_therm * therm)19457113c01SBen Skeggs g84_therm_fini(struct nvkm_therm *therm)
195e1404611SBen Skeggs {
1965718ea32SBen Skeggs 	struct nvkm_device *device = therm->subdev.device;
1975718ea32SBen Skeggs 
198e1404611SBen Skeggs 	/* Disable PTherm IRQs */
1995718ea32SBen Skeggs 	nvkm_wr32(device, 0x20000, 0x00000000);
200e1404611SBen Skeggs 
201e1404611SBen Skeggs 	/* ACK all PTherm IRQs */
2025718ea32SBen Skeggs 	nvkm_wr32(device, 0x20100, 0xffffffff);
2035718ea32SBen Skeggs 	nvkm_wr32(device, 0x1100, 0x10000); /* PBUS */
204e1404611SBen Skeggs }
205e1404611SBen Skeggs 
2069d60b9c9SKarol Herbst void
g84_therm_init(struct nvkm_therm * therm)20757113c01SBen Skeggs g84_therm_init(struct nvkm_therm *therm)
20857113c01SBen Skeggs {
20957113c01SBen Skeggs 	g84_sensor_setup(therm);
21057113c01SBen Skeggs }
21157113c01SBen Skeggs 
21257113c01SBen Skeggs static const struct nvkm_therm_func
21357113c01SBen Skeggs g84_therm = {
214e1404611SBen Skeggs 	.init = g84_therm_init,
215e1404611SBen Skeggs 	.fini = g84_therm_fini,
21657113c01SBen Skeggs 	.intr = g84_therm_intr,
21757113c01SBen Skeggs 	.pwm_ctrl = nv50_fan_pwm_ctrl,
21857113c01SBen Skeggs 	.pwm_get = nv50_fan_pwm_get,
21957113c01SBen Skeggs 	.pwm_set = nv50_fan_pwm_set,
22057113c01SBen Skeggs 	.pwm_clock = nv50_fan_pwm_clock,
22157113c01SBen Skeggs 	.temp_get = g84_temp_get,
22257113c01SBen Skeggs 	.program_alarms = g84_therm_program_alarms,
223e1404611SBen Skeggs };
22457113c01SBen Skeggs 
22557113c01SBen Skeggs int
g84_therm_new(struct nvkm_device * device,enum nvkm_subdev_type type,int inst,struct nvkm_therm ** ptherm)226*0aec69c7SBen Skeggs g84_therm_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst,
227*0aec69c7SBen Skeggs 	      struct nvkm_therm **ptherm)
22857113c01SBen Skeggs {
22957113c01SBen Skeggs 	struct nvkm_therm *therm;
23057113c01SBen Skeggs 	int ret;
23157113c01SBen Skeggs 
232*0aec69c7SBen Skeggs 	ret = nvkm_therm_new_(&g84_therm, device, type, inst, &therm);
23357113c01SBen Skeggs 	*ptherm = therm;
23457113c01SBen Skeggs 	if (ret)
23557113c01SBen Skeggs 		return ret;
23657113c01SBen Skeggs 
23757113c01SBen Skeggs 	/* init the thresholds */
23857113c01SBen Skeggs 	nvkm_therm_sensor_set_threshold_state(therm, NVKM_THERM_THRS_SHUTDOWN,
23957113c01SBen Skeggs 						     NVKM_THERM_THRS_LOWER);
24057113c01SBen Skeggs 	nvkm_therm_sensor_set_threshold_state(therm, NVKM_THERM_THRS_FANBOOST,
24157113c01SBen Skeggs 						     NVKM_THERM_THRS_LOWER);
24257113c01SBen Skeggs 	nvkm_therm_sensor_set_threshold_state(therm, NVKM_THERM_THRS_CRITICAL,
24357113c01SBen Skeggs 						     NVKM_THERM_THRS_LOWER);
24457113c01SBen Skeggs 	nvkm_therm_sensor_set_threshold_state(therm, NVKM_THERM_THRS_DOWNCLOCK,
24557113c01SBen Skeggs 						     NVKM_THERM_THRS_LOWER);
24657113c01SBen Skeggs 	return 0;
24757113c01SBen Skeggs }
248