Back to home page

OSCL-LXR

 
 

    


0001 /*
0002  * Copyright 2012 Red Hat Inc.
0003  *
0004  * Permission is hereby granted, free of charge, to any person obtaining a
0005  * copy of this software and associated documentation files (the "Software"),
0006  * to deal in the Software without restriction, including without limitation
0007  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
0008  * and/or sell copies of the Software, and to permit persons to whom the
0009  * Software is furnished to do so, subject to the following conditions:
0010  *
0011  * The above copyright notice and this permission notice shall be included in
0012  * all copies or substantial portions of the Software.
0013  *
0014  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
0015  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
0016  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
0017  * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
0018  * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
0019  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
0020  * OTHER DEALINGS IN THE SOFTWARE.
0021  *
0022  * Authors: Ben Skeggs
0023  *      Martin Peres
0024  */
0025 #include "priv.h"
0026 
0027 enum nv40_sensor_style { INVALID_STYLE = -1, OLD_STYLE = 0, NEW_STYLE = 1 };
0028 
0029 static enum nv40_sensor_style
0030 nv40_sensor_style(struct nvkm_therm *therm)
0031 {
0032     switch (therm->subdev.device->chipset) {
0033     case 0x43:
0034     case 0x44:
0035     case 0x4a:
0036     case 0x47:
0037         return OLD_STYLE;
0038     case 0x46:
0039     case 0x49:
0040     case 0x4b:
0041     case 0x4e:
0042     case 0x4c:
0043     case 0x67:
0044     case 0x68:
0045     case 0x63:
0046         return NEW_STYLE;
0047     default:
0048         return INVALID_STYLE;
0049     }
0050 }
0051 
0052 static int
0053 nv40_sensor_setup(struct nvkm_therm *therm)
0054 {
0055     struct nvkm_device *device = therm->subdev.device;
0056     enum nv40_sensor_style style = nv40_sensor_style(therm);
0057 
0058     /* enable ADC readout and disable the ALARM threshold */
0059     if (style == NEW_STYLE) {
0060         nvkm_mask(device, 0x15b8, 0x80000000, 0);
0061         nvkm_wr32(device, 0x15b0, 0x80003fff);
0062         mdelay(20); /* wait for the temperature to stabilize */
0063         return nvkm_rd32(device, 0x15b4) & 0x3fff;
0064     } else if (style == OLD_STYLE) {
0065         nvkm_wr32(device, 0x15b0, 0xff);
0066         mdelay(20); /* wait for the temperature to stabilize */
0067         return nvkm_rd32(device, 0x15b4) & 0xff;
0068     } else
0069         return -ENODEV;
0070 }
0071 
0072 static int
0073 nv40_temp_get(struct nvkm_therm *therm)
0074 {
0075     struct nvkm_device *device = therm->subdev.device;
0076     struct nvbios_therm_sensor *sensor = &therm->bios_sensor;
0077     enum nv40_sensor_style style = nv40_sensor_style(therm);
0078     int core_temp;
0079 
0080     if (style == NEW_STYLE) {
0081         nvkm_wr32(device, 0x15b0, 0x80003fff);
0082         core_temp = nvkm_rd32(device, 0x15b4) & 0x3fff;
0083     } else if (style == OLD_STYLE) {
0084         nvkm_wr32(device, 0x15b0, 0xff);
0085         core_temp = nvkm_rd32(device, 0x15b4) & 0xff;
0086     } else
0087         return -ENODEV;
0088 
0089     /* if the slope or the offset is unset, do no use the sensor */
0090     if (!sensor->slope_div || !sensor->slope_mult ||
0091         !sensor->offset_num || !sensor->offset_den)
0092         return -ENODEV;
0093 
0094     core_temp = core_temp * sensor->slope_mult / sensor->slope_div;
0095     core_temp = core_temp + sensor->offset_num / sensor->offset_den;
0096     core_temp = core_temp + sensor->offset_constant - 8;
0097 
0098     /* reserve negative temperatures for errors */
0099     if (core_temp < 0)
0100         core_temp = 0;
0101 
0102     return core_temp;
0103 }
0104 
0105 static int
0106 nv40_fan_pwm_ctrl(struct nvkm_therm *therm, int line, bool enable)
0107 {
0108     struct nvkm_subdev *subdev = &therm->subdev;
0109     struct nvkm_device *device = subdev->device;
0110     u32 mask = enable ? 0x80000000 : 0x00000000;
0111     if      (line == 2) nvkm_mask(device, 0x0010f0, 0x80000000, mask);
0112     else if (line == 9) nvkm_mask(device, 0x0015f4, 0x80000000, mask);
0113     else {
0114         nvkm_error(subdev, "unknown pwm ctrl for gpio %d\n", line);
0115         return -ENODEV;
0116     }
0117     return 0;
0118 }
0119 
0120 static int
0121 nv40_fan_pwm_get(struct nvkm_therm *therm, int line, u32 *divs, u32 *duty)
0122 {
0123     struct nvkm_subdev *subdev = &therm->subdev;
0124     struct nvkm_device *device = subdev->device;
0125     if (line == 2) {
0126         u32 reg = nvkm_rd32(device, 0x0010f0);
0127         if (reg & 0x80000000) {
0128             *duty = (reg & 0x7fff0000) >> 16;
0129             *divs = (reg & 0x00007fff);
0130             return 0;
0131         }
0132     } else
0133     if (line == 9) {
0134         u32 reg = nvkm_rd32(device, 0x0015f4);
0135         if (reg & 0x80000000) {
0136             *divs = nvkm_rd32(device, 0x0015f8);
0137             *duty = (reg & 0x7fffffff);
0138             return 0;
0139         }
0140     } else {
0141         nvkm_error(subdev, "unknown pwm ctrl for gpio %d\n", line);
0142         return -ENODEV;
0143     }
0144 
0145     return -EINVAL;
0146 }
0147 
0148 static int
0149 nv40_fan_pwm_set(struct nvkm_therm *therm, int line, u32 divs, u32 duty)
0150 {
0151     struct nvkm_subdev *subdev = &therm->subdev;
0152     struct nvkm_device *device = subdev->device;
0153     if (line == 2) {
0154         nvkm_mask(device, 0x0010f0, 0x7fff7fff, (duty << 16) | divs);
0155     } else
0156     if (line == 9) {
0157         nvkm_wr32(device, 0x0015f8, divs);
0158         nvkm_mask(device, 0x0015f4, 0x7fffffff, duty);
0159     } else {
0160         nvkm_error(subdev, "unknown pwm ctrl for gpio %d\n", line);
0161         return -ENODEV;
0162     }
0163 
0164     return 0;
0165 }
0166 
0167 void
0168 nv40_therm_intr(struct nvkm_therm *therm)
0169 {
0170     struct nvkm_subdev *subdev = &therm->subdev;
0171     struct nvkm_device *device = subdev->device;
0172     uint32_t stat = nvkm_rd32(device, 0x1100);
0173 
0174     /* traitement */
0175 
0176     /* ack all IRQs */
0177     nvkm_wr32(device, 0x1100, 0x70000);
0178 
0179     nvkm_error(subdev, "THERM received an IRQ: stat = %x\n", stat);
0180 }
0181 
0182 static void
0183 nv40_therm_init(struct nvkm_therm *therm)
0184 {
0185     nv40_sensor_setup(therm);
0186 }
0187 
0188 static const struct nvkm_therm_func
0189 nv40_therm = {
0190     .init = nv40_therm_init,
0191     .intr = nv40_therm_intr,
0192     .pwm_ctrl = nv40_fan_pwm_ctrl,
0193     .pwm_get = nv40_fan_pwm_get,
0194     .pwm_set = nv40_fan_pwm_set,
0195     .temp_get = nv40_temp_get,
0196     .program_alarms = nvkm_therm_program_alarms_polling,
0197 };
0198 
0199 int
0200 nv40_therm_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst,
0201            struct nvkm_therm **ptherm)
0202 {
0203     return nvkm_therm_new_(&nv40_therm, device, type, inst, ptherm);
0204 }