Home | History | Annotate | Line # | Download | only in therm
      1 /*	$NetBSD: nouveau_nvkm_subdev_therm_nv40.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_nv40.c,v 1.3 2021/12/18 23:45:41 riastradh Exp $");
     29 
     30 #include "priv.h"
     31 
     32 enum nv40_sensor_style { INVALID_STYLE = -1, OLD_STYLE = 0, NEW_STYLE = 1 };
     33 
     34 static enum nv40_sensor_style
     35 nv40_sensor_style(struct nvkm_therm *therm)
     36 {
     37 	switch (therm->subdev.device->chipset) {
     38 	case 0x43:
     39 	case 0x44:
     40 	case 0x4a:
     41 	case 0x47:
     42 		return OLD_STYLE;
     43 	case 0x46:
     44 	case 0x49:
     45 	case 0x4b:
     46 	case 0x4e:
     47 	case 0x4c:
     48 	case 0x67:
     49 	case 0x68:
     50 	case 0x63:
     51 		return NEW_STYLE;
     52 	default:
     53 		return INVALID_STYLE;
     54 	}
     55 }
     56 
     57 static int
     58 nv40_sensor_setup(struct nvkm_therm *therm)
     59 {
     60 	struct nvkm_device *device = therm->subdev.device;
     61 	enum nv40_sensor_style style = nv40_sensor_style(therm);
     62 
     63 	/* enable ADC readout and disable the ALARM threshold */
     64 	if (style == NEW_STYLE) {
     65 		nvkm_mask(device, 0x15b8, 0x80000000, 0);
     66 		nvkm_wr32(device, 0x15b0, 0x80003fff);
     67 		mdelay(20); /* wait for the temperature to stabilize */
     68 		return nvkm_rd32(device, 0x15b4) & 0x3fff;
     69 	} else if (style == OLD_STYLE) {
     70 		nvkm_wr32(device, 0x15b0, 0xff);
     71 		mdelay(20); /* wait for the temperature to stabilize */
     72 		return nvkm_rd32(device, 0x15b4) & 0xff;
     73 	} else
     74 		return -ENODEV;
     75 }
     76 
     77 static int
     78 nv40_temp_get(struct nvkm_therm *therm)
     79 {
     80 	struct nvkm_device *device = therm->subdev.device;
     81 	struct nvbios_therm_sensor *sensor = &therm->bios_sensor;
     82 	enum nv40_sensor_style style = nv40_sensor_style(therm);
     83 	int core_temp;
     84 
     85 	if (style == NEW_STYLE) {
     86 		nvkm_wr32(device, 0x15b0, 0x80003fff);
     87 		core_temp = nvkm_rd32(device, 0x15b4) & 0x3fff;
     88 	} else if (style == OLD_STYLE) {
     89 		nvkm_wr32(device, 0x15b0, 0xff);
     90 		core_temp = nvkm_rd32(device, 0x15b4) & 0xff;
     91 	} else
     92 		return -ENODEV;
     93 
     94 	/* if the slope or the offset is unset, do no use the sensor */
     95 	if (!sensor->slope_div || !sensor->slope_mult ||
     96 	    !sensor->offset_num || !sensor->offset_den)
     97 	    return -ENODEV;
     98 
     99 	core_temp = core_temp * sensor->slope_mult / sensor->slope_div;
    100 	core_temp = core_temp + sensor->offset_num / sensor->offset_den;
    101 	core_temp = core_temp + sensor->offset_constant - 8;
    102 
    103 	/* reserve negative temperatures for errors */
    104 	if (core_temp < 0)
    105 		core_temp = 0;
    106 
    107 	return core_temp;
    108 }
    109 
    110 static int
    111 nv40_fan_pwm_ctrl(struct nvkm_therm *therm, int line, bool enable)
    112 {
    113 	struct nvkm_subdev *subdev = &therm->subdev;
    114 	struct nvkm_device *device = subdev->device;
    115 	u32 mask = enable ? 0x80000000 : 0x00000000;
    116 	if      (line == 2) nvkm_mask(device, 0x0010f0, 0x80000000, mask);
    117 	else if (line == 9) nvkm_mask(device, 0x0015f4, 0x80000000, mask);
    118 	else {
    119 		nvkm_error(subdev, "unknown pwm ctrl for gpio %d\n", line);
    120 		return -ENODEV;
    121 	}
    122 	return 0;
    123 }
    124 
    125 static int
    126 nv40_fan_pwm_get(struct nvkm_therm *therm, int line, u32 *divs, u32 *duty)
    127 {
    128 	struct nvkm_subdev *subdev = &therm->subdev;
    129 	struct nvkm_device *device = subdev->device;
    130 	if (line == 2) {
    131 		u32 reg = nvkm_rd32(device, 0x0010f0);
    132 		if (reg & 0x80000000) {
    133 			*duty = (reg & 0x7fff0000) >> 16;
    134 			*divs = (reg & 0x00007fff);
    135 			return 0;
    136 		}
    137 	} else
    138 	if (line == 9) {
    139 		u32 reg = nvkm_rd32(device, 0x0015f4);
    140 		if (reg & 0x80000000) {
    141 			*divs = nvkm_rd32(device, 0x0015f8);
    142 			*duty = (reg & 0x7fffffff);
    143 			return 0;
    144 		}
    145 	} else {
    146 		nvkm_error(subdev, "unknown pwm ctrl for gpio %d\n", line);
    147 		return -ENODEV;
    148 	}
    149 
    150 	return -EINVAL;
    151 }
    152 
    153 static int
    154 nv40_fan_pwm_set(struct nvkm_therm *therm, int line, u32 divs, u32 duty)
    155 {
    156 	struct nvkm_subdev *subdev = &therm->subdev;
    157 	struct nvkm_device *device = subdev->device;
    158 	if (line == 2) {
    159 		nvkm_mask(device, 0x0010f0, 0x7fff7fff, (duty << 16) | divs);
    160 	} else
    161 	if (line == 9) {
    162 		nvkm_wr32(device, 0x0015f8, divs);
    163 		nvkm_mask(device, 0x0015f4, 0x7fffffff, duty);
    164 	} else {
    165 		nvkm_error(subdev, "unknown pwm ctrl for gpio %d\n", line);
    166 		return -ENODEV;
    167 	}
    168 
    169 	return 0;
    170 }
    171 
    172 void
    173 nv40_therm_intr(struct nvkm_therm *therm)
    174 {
    175 	struct nvkm_subdev *subdev = &therm->subdev;
    176 	struct nvkm_device *device = subdev->device;
    177 	uint32_t stat = nvkm_rd32(device, 0x1100);
    178 
    179 	/* traitement */
    180 
    181 	/* ack all IRQs */
    182 	nvkm_wr32(device, 0x1100, 0x70000);
    183 
    184 	nvkm_error(subdev, "THERM received an IRQ: stat = %x\n", stat);
    185 }
    186 
    187 static void
    188 nv40_therm_init(struct nvkm_therm *therm)
    189 {
    190 	nv40_sensor_setup(therm);
    191 }
    192 
    193 static const struct nvkm_therm_func
    194 nv40_therm = {
    195 	.init = nv40_therm_init,
    196 	.intr = nv40_therm_intr,
    197 	.pwm_ctrl = nv40_fan_pwm_ctrl,
    198 	.pwm_get = nv40_fan_pwm_get,
    199 	.pwm_set = nv40_fan_pwm_set,
    200 	.temp_get = nv40_temp_get,
    201 	.program_alarms = nvkm_therm_program_alarms_polling,
    202 };
    203 
    204 int
    205 nv40_therm_new(struct nvkm_device *device, int index,
    206 	       struct nvkm_therm **ptherm)
    207 {
    208 	return nvkm_therm_new_(&nv40_therm, device, index, ptherm);
    209 }
    210