xref: /openbmc/linux/drivers/gpu/drm/nouveau/nvkm/subdev/therm/nv40.c (revision d0034a7a4ac7fae708146ac0059b9c47a1543f0d)
1c39f472eSBen Skeggs /*
2c39f472eSBen Skeggs  * Copyright 2012 Red Hat Inc.
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: Ben Skeggs
23c39f472eSBen Skeggs  * 	    Martin Peres
24c39f472eSBen Skeggs  */
25c39f472eSBen Skeggs #include "priv.h"
26c39f472eSBen Skeggs 
27c39f472eSBen Skeggs enum nv40_sensor_style { INVALID_STYLE = -1, OLD_STYLE = 0, NEW_STYLE = 1 };
28c39f472eSBen Skeggs 
29c39f472eSBen Skeggs static enum nv40_sensor_style
nv40_sensor_style(struct nvkm_therm * therm)30e1404611SBen Skeggs nv40_sensor_style(struct nvkm_therm *therm)
31c39f472eSBen Skeggs {
3257113c01SBen Skeggs 	switch (therm->subdev.device->chipset) {
33c39f472eSBen Skeggs 	case 0x43:
34c39f472eSBen Skeggs 	case 0x44:
35c39f472eSBen Skeggs 	case 0x4a:
36c39f472eSBen Skeggs 	case 0x47:
37c39f472eSBen Skeggs 		return OLD_STYLE;
38c39f472eSBen Skeggs 	case 0x46:
39c39f472eSBen Skeggs 	case 0x49:
40c39f472eSBen Skeggs 	case 0x4b:
41c39f472eSBen Skeggs 	case 0x4e:
42c39f472eSBen Skeggs 	case 0x4c:
43c39f472eSBen Skeggs 	case 0x67:
44c39f472eSBen Skeggs 	case 0x68:
45c39f472eSBen Skeggs 	case 0x63:
46c39f472eSBen Skeggs 		return NEW_STYLE;
47c39f472eSBen Skeggs 	default:
48c39f472eSBen Skeggs 		return INVALID_STYLE;
49c39f472eSBen Skeggs 	}
50c39f472eSBen Skeggs }
51c39f472eSBen Skeggs 
52c39f472eSBen Skeggs static int
nv40_sensor_setup(struct nvkm_therm * therm)53e1404611SBen Skeggs nv40_sensor_setup(struct nvkm_therm *therm)
54c39f472eSBen Skeggs {
555718ea32SBen Skeggs 	struct nvkm_device *device = therm->subdev.device;
56c39f472eSBen Skeggs 	enum nv40_sensor_style style = nv40_sensor_style(therm);
57c39f472eSBen Skeggs 
58c39f472eSBen Skeggs 	/* enable ADC readout and disable the ALARM threshold */
59c39f472eSBen Skeggs 	if (style == NEW_STYLE) {
605718ea32SBen Skeggs 		nvkm_mask(device, 0x15b8, 0x80000000, 0);
615718ea32SBen Skeggs 		nvkm_wr32(device, 0x15b0, 0x80003fff);
62c39f472eSBen Skeggs 		mdelay(20); /* wait for the temperature to stabilize */
635718ea32SBen Skeggs 		return nvkm_rd32(device, 0x15b4) & 0x3fff;
64c39f472eSBen Skeggs 	} else if (style == OLD_STYLE) {
655718ea32SBen Skeggs 		nvkm_wr32(device, 0x15b0, 0xff);
66c39f472eSBen Skeggs 		mdelay(20); /* wait for the temperature to stabilize */
675718ea32SBen Skeggs 		return nvkm_rd32(device, 0x15b4) & 0xff;
68c39f472eSBen Skeggs 	} else
69c39f472eSBen Skeggs 		return -ENODEV;
70c39f472eSBen Skeggs }
71c39f472eSBen Skeggs 
72c39f472eSBen Skeggs static int
nv40_temp_get(struct nvkm_therm * therm)7357113c01SBen Skeggs nv40_temp_get(struct nvkm_therm *therm)
74c39f472eSBen Skeggs {
7557113c01SBen Skeggs 	struct nvkm_device *device = therm->subdev.device;
76da06b46bSBen Skeggs 	struct nvbios_therm_sensor *sensor = &therm->bios_sensor;
7757113c01SBen Skeggs 	enum nv40_sensor_style style = nv40_sensor_style(therm);
78c39f472eSBen Skeggs 	int core_temp;
79c39f472eSBen Skeggs 
80c39f472eSBen Skeggs 	if (style == NEW_STYLE) {
815718ea32SBen Skeggs 		nvkm_wr32(device, 0x15b0, 0x80003fff);
825718ea32SBen Skeggs 		core_temp = nvkm_rd32(device, 0x15b4) & 0x3fff;
83c39f472eSBen Skeggs 	} else if (style == OLD_STYLE) {
845718ea32SBen Skeggs 		nvkm_wr32(device, 0x15b0, 0xff);
855718ea32SBen Skeggs 		core_temp = nvkm_rd32(device, 0x15b4) & 0xff;
86c39f472eSBen Skeggs 	} else
87c39f472eSBen Skeggs 		return -ENODEV;
88c39f472eSBen Skeggs 
89c39f472eSBen Skeggs 	/* if the slope or the offset is unset, do no use the sensor */
90c39f472eSBen Skeggs 	if (!sensor->slope_div || !sensor->slope_mult ||
91c39f472eSBen Skeggs 	    !sensor->offset_num || !sensor->offset_den)
92c39f472eSBen Skeggs 	    return -ENODEV;
93c39f472eSBen Skeggs 
94c39f472eSBen Skeggs 	core_temp = core_temp * sensor->slope_mult / sensor->slope_div;
95c39f472eSBen Skeggs 	core_temp = core_temp + sensor->offset_num / sensor->offset_den;
96c39f472eSBen Skeggs 	core_temp = core_temp + sensor->offset_constant - 8;
97c39f472eSBen Skeggs 
98c39f472eSBen Skeggs 	/* reserve negative temperatures for errors */
99c39f472eSBen Skeggs 	if (core_temp < 0)
100c39f472eSBen Skeggs 		core_temp = 0;
101c39f472eSBen Skeggs 
102c39f472eSBen Skeggs 	return core_temp;
103c39f472eSBen Skeggs }
104c39f472eSBen Skeggs 
105c39f472eSBen Skeggs static int
nv40_fan_pwm_ctrl(struct nvkm_therm * therm,int line,bool enable)106e1404611SBen Skeggs nv40_fan_pwm_ctrl(struct nvkm_therm *therm, int line, bool enable)
107c39f472eSBen Skeggs {
108b3c418bbSBen Skeggs 	struct nvkm_subdev *subdev = &therm->subdev;
109b3c418bbSBen Skeggs 	struct nvkm_device *device = subdev->device;
110cfb4f929SIlia Mirkin 	u32 mask = enable ? 0x80000000 : 0x00000000;
1115718ea32SBen Skeggs 	if      (line == 2) nvkm_mask(device, 0x0010f0, 0x80000000, mask);
1125718ea32SBen Skeggs 	else if (line == 9) nvkm_mask(device, 0x0015f4, 0x80000000, mask);
113c39f472eSBen Skeggs 	else {
114b3c418bbSBen Skeggs 		nvkm_error(subdev, "unknown pwm ctrl for gpio %d\n", line);
115c39f472eSBen Skeggs 		return -ENODEV;
116c39f472eSBen Skeggs 	}
117c39f472eSBen Skeggs 	return 0;
118c39f472eSBen Skeggs }
119c39f472eSBen Skeggs 
120c39f472eSBen Skeggs static int
nv40_fan_pwm_get(struct nvkm_therm * therm,int line,u32 * divs,u32 * duty)121e1404611SBen Skeggs nv40_fan_pwm_get(struct nvkm_therm *therm, int line, u32 *divs, u32 *duty)
122c39f472eSBen Skeggs {
123b3c418bbSBen Skeggs 	struct nvkm_subdev *subdev = &therm->subdev;
124b3c418bbSBen Skeggs 	struct nvkm_device *device = subdev->device;
125c39f472eSBen Skeggs 	if (line == 2) {
1265718ea32SBen Skeggs 		u32 reg = nvkm_rd32(device, 0x0010f0);
127c39f472eSBen Skeggs 		if (reg & 0x80000000) {
128c39f472eSBen Skeggs 			*duty = (reg & 0x7fff0000) >> 16;
129c39f472eSBen Skeggs 			*divs = (reg & 0x00007fff);
130c39f472eSBen Skeggs 			return 0;
131c39f472eSBen Skeggs 		}
132c39f472eSBen Skeggs 	} else
133c39f472eSBen Skeggs 	if (line == 9) {
1345718ea32SBen Skeggs 		u32 reg = nvkm_rd32(device, 0x0015f4);
135c39f472eSBen Skeggs 		if (reg & 0x80000000) {
1365718ea32SBen Skeggs 			*divs = nvkm_rd32(device, 0x0015f8);
137c39f472eSBen Skeggs 			*duty = (reg & 0x7fffffff);
138c39f472eSBen Skeggs 			return 0;
139c39f472eSBen Skeggs 		}
140c39f472eSBen Skeggs 	} else {
141b3c418bbSBen Skeggs 		nvkm_error(subdev, "unknown pwm ctrl for gpio %d\n", line);
142c39f472eSBen Skeggs 		return -ENODEV;
143c39f472eSBen Skeggs 	}
144c39f472eSBen Skeggs 
145c39f472eSBen Skeggs 	return -EINVAL;
146c39f472eSBen Skeggs }
147c39f472eSBen Skeggs 
148c39f472eSBen Skeggs static int
nv40_fan_pwm_set(struct nvkm_therm * therm,int line,u32 divs,u32 duty)149e1404611SBen Skeggs nv40_fan_pwm_set(struct nvkm_therm *therm, int line, u32 divs, u32 duty)
150c39f472eSBen Skeggs {
151b3c418bbSBen Skeggs 	struct nvkm_subdev *subdev = &therm->subdev;
152b3c418bbSBen Skeggs 	struct nvkm_device *device = subdev->device;
153c39f472eSBen Skeggs 	if (line == 2) {
1545718ea32SBen Skeggs 		nvkm_mask(device, 0x0010f0, 0x7fff7fff, (duty << 16) | divs);
155c39f472eSBen Skeggs 	} else
156c39f472eSBen Skeggs 	if (line == 9) {
1575718ea32SBen Skeggs 		nvkm_wr32(device, 0x0015f8, divs);
1585718ea32SBen Skeggs 		nvkm_mask(device, 0x0015f4, 0x7fffffff, duty);
159c39f472eSBen Skeggs 	} else {
160b3c418bbSBen Skeggs 		nvkm_error(subdev, "unknown pwm ctrl for gpio %d\n", line);
161c39f472eSBen Skeggs 		return -ENODEV;
162c39f472eSBen Skeggs 	}
163c39f472eSBen Skeggs 
164c39f472eSBen Skeggs 	return 0;
165c39f472eSBen Skeggs }
166c39f472eSBen Skeggs 
167c39f472eSBen Skeggs void
nv40_therm_intr(struct nvkm_therm * therm)16857113c01SBen Skeggs nv40_therm_intr(struct nvkm_therm *therm)
169c39f472eSBen Skeggs {
17057113c01SBen Skeggs 	struct nvkm_subdev *subdev = &therm->subdev;
17157113c01SBen Skeggs 	struct nvkm_device *device = subdev->device;
1725718ea32SBen Skeggs 	uint32_t stat = nvkm_rd32(device, 0x1100);
173c39f472eSBen Skeggs 
174c39f472eSBen Skeggs 	/* traitement */
175c39f472eSBen Skeggs 
176c39f472eSBen Skeggs 	/* ack all IRQs */
1775718ea32SBen Skeggs 	nvkm_wr32(device, 0x1100, 0x70000);
178c39f472eSBen Skeggs 
179b3c418bbSBen Skeggs 	nvkm_error(subdev, "THERM received an IRQ: stat = %x\n", stat);
180c39f472eSBen Skeggs }
181c39f472eSBen Skeggs 
18257113c01SBen Skeggs static void
nv40_therm_init(struct nvkm_therm * therm)18357113c01SBen Skeggs nv40_therm_init(struct nvkm_therm *therm)
184c39f472eSBen Skeggs {
185c39f472eSBen Skeggs 	nv40_sensor_setup(therm);
186c39f472eSBen Skeggs }
187c39f472eSBen Skeggs 
18857113c01SBen Skeggs static const struct nvkm_therm_func
18957113c01SBen Skeggs nv40_therm = {
190c39f472eSBen Skeggs 	.init = nv40_therm_init,
19157113c01SBen Skeggs 	.intr = nv40_therm_intr,
19257113c01SBen Skeggs 	.pwm_ctrl = nv40_fan_pwm_ctrl,
19357113c01SBen Skeggs 	.pwm_get = nv40_fan_pwm_get,
19457113c01SBen Skeggs 	.pwm_set = nv40_fan_pwm_set,
19557113c01SBen Skeggs 	.temp_get = nv40_temp_get,
19657113c01SBen Skeggs 	.program_alarms = nvkm_therm_program_alarms_polling,
197c39f472eSBen Skeggs };
19857113c01SBen Skeggs 
19957113c01SBen Skeggs int
nv40_therm_new(struct nvkm_device * device,enum nvkm_subdev_type type,int inst,struct nvkm_therm ** ptherm)200*0aec69c7SBen Skeggs nv40_therm_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst,
20157113c01SBen Skeggs 	       struct nvkm_therm **ptherm)
20257113c01SBen Skeggs {
203*0aec69c7SBen Skeggs 	return nvkm_therm_new_(&nv40_therm, device, type, inst, ptherm);
20457113c01SBen Skeggs }
205