xref: /linux/drivers/gpu/drm/nouveau/nvkm/subdev/therm/nv40.c (revision 4de93a086eb0315f0bd8e1d6da40186842670b57)
1 /*
2  * Copyright 2012 Red Hat Inc.
3  *
4  * Permission is hereby granted, free of charge, to any person obtaining a
5  * copy of this software and associated documentation files (the "Software"),
6  * to deal in the Software without restriction, including without limitation
7  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8  * and/or sell copies of the Software, and to permit persons to whom the
9  * Software is furnished to do so, subject to the following conditions:
10  *
11  * The above copyright notice and this permission notice shall be included in
12  * all copies or substantial portions of the Software.
13  *
14  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
17  * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
18  * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
19  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
20  * OTHER DEALINGS IN THE SOFTWARE.
21  *
22  * Authors: Ben Skeggs
23  * 	    Martin Peres
24  */
25 #include "priv.h"
26 
27 enum nv40_sensor_style { INVALID_STYLE = -1, OLD_STYLE = 0, NEW_STYLE = 1 };
28 
29 static enum nv40_sensor_style
30 nv40_sensor_style(struct nvkm_therm *therm)
31 {
32 	struct nvkm_device *device = nv_device(therm);
33 
34 	switch (device->chipset) {
35 	case 0x43:
36 	case 0x44:
37 	case 0x4a:
38 	case 0x47:
39 		return OLD_STYLE;
40 
41 	case 0x46:
42 	case 0x49:
43 	case 0x4b:
44 	case 0x4e:
45 	case 0x4c:
46 	case 0x67:
47 	case 0x68:
48 	case 0x63:
49 		return NEW_STYLE;
50 	default:
51 		return INVALID_STYLE;
52 	}
53 }
54 
55 static int
56 nv40_sensor_setup(struct nvkm_therm *therm)
57 {
58 	enum nv40_sensor_style style = nv40_sensor_style(therm);
59 
60 	/* enable ADC readout and disable the ALARM threshold */
61 	if (style == NEW_STYLE) {
62 		nv_mask(therm, 0x15b8, 0x80000000, 0);
63 		nv_wr32(therm, 0x15b0, 0x80003fff);
64 		mdelay(20); /* wait for the temperature to stabilize */
65 		return nv_rd32(therm, 0x15b4) & 0x3fff;
66 	} else if (style == OLD_STYLE) {
67 		nv_wr32(therm, 0x15b0, 0xff);
68 		mdelay(20); /* wait for the temperature to stabilize */
69 		return nv_rd32(therm, 0x15b4) & 0xff;
70 	} else
71 		return -ENODEV;
72 }
73 
74 static int
75 nv40_temp_get(struct nvkm_therm *obj)
76 {
77 	struct nvkm_therm_priv *therm = container_of(obj, typeof(*therm), base);
78 	struct nvbios_therm_sensor *sensor = &therm->bios_sensor;
79 	enum nv40_sensor_style style = nv40_sensor_style(&therm->base);
80 	int core_temp;
81 
82 	if (style == NEW_STYLE) {
83 		nv_wr32(therm, 0x15b0, 0x80003fff);
84 		core_temp = nv_rd32(therm, 0x15b4) & 0x3fff;
85 	} else if (style == OLD_STYLE) {
86 		nv_wr32(therm, 0x15b0, 0xff);
87 		core_temp = nv_rd32(therm, 0x15b4) & 0xff;
88 	} else
89 		return -ENODEV;
90 
91 	/* if the slope or the offset is unset, do no use the sensor */
92 	if (!sensor->slope_div || !sensor->slope_mult ||
93 	    !sensor->offset_num || !sensor->offset_den)
94 	    return -ENODEV;
95 
96 	core_temp = core_temp * sensor->slope_mult / sensor->slope_div;
97 	core_temp = core_temp + sensor->offset_num / sensor->offset_den;
98 	core_temp = core_temp + sensor->offset_constant - 8;
99 
100 	/* reserve negative temperatures for errors */
101 	if (core_temp < 0)
102 		core_temp = 0;
103 
104 	return core_temp;
105 }
106 
107 static int
108 nv40_fan_pwm_ctrl(struct nvkm_therm *therm, int line, bool enable)
109 {
110 	u32 mask = enable ? 0x80000000 : 0x0000000;
111 	if      (line == 2) nv_mask(therm, 0x0010f0, 0x80000000, mask);
112 	else if (line == 9) nv_mask(therm, 0x0015f4, 0x80000000, mask);
113 	else {
114 		nv_error(therm, "unknown pwm ctrl for gpio %d\n", line);
115 		return -ENODEV;
116 	}
117 	return 0;
118 }
119 
120 static int
121 nv40_fan_pwm_get(struct nvkm_therm *therm, int line, u32 *divs, u32 *duty)
122 {
123 	if (line == 2) {
124 		u32 reg = nv_rd32(therm, 0x0010f0);
125 		if (reg & 0x80000000) {
126 			*duty = (reg & 0x7fff0000) >> 16;
127 			*divs = (reg & 0x00007fff);
128 			return 0;
129 		}
130 	} else
131 	if (line == 9) {
132 		u32 reg = nv_rd32(therm, 0x0015f4);
133 		if (reg & 0x80000000) {
134 			*divs = nv_rd32(therm, 0x0015f8);
135 			*duty = (reg & 0x7fffffff);
136 			return 0;
137 		}
138 	} else {
139 		nv_error(therm, "unknown pwm ctrl for gpio %d\n", line);
140 		return -ENODEV;
141 	}
142 
143 	return -EINVAL;
144 }
145 
146 static int
147 nv40_fan_pwm_set(struct nvkm_therm *therm, int line, u32 divs, u32 duty)
148 {
149 	if (line == 2) {
150 		nv_mask(therm, 0x0010f0, 0x7fff7fff, (duty << 16) | divs);
151 	} else
152 	if (line == 9) {
153 		nv_wr32(therm, 0x0015f8, divs);
154 		nv_mask(therm, 0x0015f4, 0x7fffffff, duty);
155 	} else {
156 		nv_error(therm, "unknown pwm ctrl for gpio %d\n", line);
157 		return -ENODEV;
158 	}
159 
160 	return 0;
161 }
162 
163 void
164 nv40_therm_intr(struct nvkm_subdev *subdev)
165 {
166 	struct nvkm_therm *therm = nvkm_therm(subdev);
167 	uint32_t stat = nv_rd32(therm, 0x1100);
168 
169 	/* traitement */
170 
171 	/* ack all IRQs */
172 	nv_wr32(therm, 0x1100, 0x70000);
173 
174 	nv_error(therm, "THERM received an IRQ: stat = %x\n", stat);
175 }
176 
177 static int
178 nv40_therm_ctor(struct nvkm_object *parent,
179 		struct nvkm_object *engine,
180 		struct nvkm_oclass *oclass, void *data, u32 size,
181 		struct nvkm_object **pobject)
182 {
183 	struct nvkm_therm_priv *therm;
184 	int ret;
185 
186 	ret = nvkm_therm_create(parent, engine, oclass, &therm);
187 	*pobject = nv_object(therm);
188 	if (ret)
189 		return ret;
190 
191 	therm->base.pwm_ctrl = nv40_fan_pwm_ctrl;
192 	therm->base.pwm_get = nv40_fan_pwm_get;
193 	therm->base.pwm_set = nv40_fan_pwm_set;
194 	therm->base.temp_get = nv40_temp_get;
195 	therm->sensor.program_alarms = nvkm_therm_program_alarms_polling;
196 	nv_subdev(therm)->intr = nv40_therm_intr;
197 	return nvkm_therm_preinit(&therm->base);
198 }
199 
200 static int
201 nv40_therm_init(struct nvkm_object *object)
202 {
203 	struct nvkm_therm *therm = (void *)object;
204 
205 	nv40_sensor_setup(therm);
206 
207 	return _nvkm_therm_init(object);
208 }
209 
210 struct nvkm_oclass
211 nv40_therm_oclass = {
212 	.handle = NV_SUBDEV(THERM, 0x40),
213 	.ofuncs = &(struct nvkm_ofuncs) {
214 		.ctor = nv40_therm_ctor,
215 		.dtor = _nvkm_therm_dtor,
216 		.init = nv40_therm_init,
217 		.fini = _nvkm_therm_fini,
218 	},
219 };
220