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 static int
0028 pwm_info(struct nvkm_therm *therm, int *line, int *ctrl, int *indx)
0029 {
0030     struct nvkm_subdev *subdev = &therm->subdev;
0031 
0032     if (*line == 0x04) {
0033         *ctrl = 0x00e100;
0034         *line = 4;
0035         *indx = 0;
0036     } else
0037     if (*line == 0x09) {
0038         *ctrl = 0x00e100;
0039         *line = 9;
0040         *indx = 1;
0041     } else
0042     if (*line == 0x10) {
0043         *ctrl = 0x00e28c;
0044         *line = 0;
0045         *indx = 0;
0046     } else {
0047         nvkm_error(subdev, "unknown pwm ctrl for gpio %d\n", *line);
0048         return -ENODEV;
0049     }
0050 
0051     return 0;
0052 }
0053 
0054 int
0055 nv50_fan_pwm_ctrl(struct nvkm_therm *therm, int line, bool enable)
0056 {
0057     struct nvkm_device *device = therm->subdev.device;
0058     u32 data = enable ? 0x00000001 : 0x00000000;
0059     int ctrl, id, ret = pwm_info(therm, &line, &ctrl, &id);
0060     if (ret == 0)
0061         nvkm_mask(device, ctrl, 0x00010001 << line, data << line);
0062     return ret;
0063 }
0064 
0065 int
0066 nv50_fan_pwm_get(struct nvkm_therm *therm, int line, u32 *divs, u32 *duty)
0067 {
0068     struct nvkm_device *device = therm->subdev.device;
0069     int ctrl, id, ret = pwm_info(therm, &line, &ctrl, &id);
0070     if (ret)
0071         return ret;
0072 
0073     if (nvkm_rd32(device, ctrl) & (1 << line)) {
0074         *divs = nvkm_rd32(device, 0x00e114 + (id * 8));
0075         *duty = nvkm_rd32(device, 0x00e118 + (id * 8));
0076         return 0;
0077     }
0078 
0079     return -EINVAL;
0080 }
0081 
0082 int
0083 nv50_fan_pwm_set(struct nvkm_therm *therm, int line, u32 divs, u32 duty)
0084 {
0085     struct nvkm_device *device = therm->subdev.device;
0086     int ctrl, id, ret = pwm_info(therm, &line, &ctrl, &id);
0087     if (ret)
0088         return ret;
0089 
0090     nvkm_wr32(device, 0x00e114 + (id * 8), divs);
0091     nvkm_wr32(device, 0x00e118 + (id * 8), duty | 0x80000000);
0092     return 0;
0093 }
0094 
0095 int
0096 nv50_fan_pwm_clock(struct nvkm_therm *therm, int line)
0097 {
0098     struct nvkm_device *device = therm->subdev.device;
0099     int pwm_clock;
0100 
0101     /* determine the PWM source clock */
0102     if (device->chipset > 0x50 && device->chipset < 0x94) {
0103         u8 pwm_div = nvkm_rd32(device, 0x410c);
0104         if (nvkm_rd32(device, 0xc040) & 0x800000) {
0105             /* Use the HOST clock (100 MHz)
0106             * Where does this constant(2.4) comes from? */
0107             pwm_clock = (100000000 >> pwm_div) * 10 / 24;
0108         } else {
0109             /* Where does this constant(20) comes from? */
0110             pwm_clock = (device->crystal * 1000) >> pwm_div;
0111             pwm_clock /= 20;
0112         }
0113     } else {
0114         pwm_clock = (device->crystal * 1000) / 20;
0115     }
0116 
0117     return pwm_clock;
0118 }
0119 
0120 static void
0121 nv50_sensor_setup(struct nvkm_therm *therm)
0122 {
0123     struct nvkm_device *device = therm->subdev.device;
0124     nvkm_mask(device, 0x20010, 0x40000000, 0x0);
0125     mdelay(20); /* wait for the temperature to stabilize */
0126 }
0127 
0128 static int
0129 nv50_temp_get(struct nvkm_therm *therm)
0130 {
0131     struct nvkm_device *device = therm->subdev.device;
0132     struct nvbios_therm_sensor *sensor = &therm->bios_sensor;
0133     int core_temp;
0134 
0135     core_temp = nvkm_rd32(device, 0x20014) & 0x3fff;
0136 
0137     /* if the slope or the offset is unset, do no use the sensor */
0138     if (!sensor->slope_div || !sensor->slope_mult ||
0139         !sensor->offset_num || !sensor->offset_den)
0140         return -ENODEV;
0141 
0142     core_temp = core_temp * sensor->slope_mult / sensor->slope_div;
0143     core_temp = core_temp + sensor->offset_num / sensor->offset_den;
0144     core_temp = core_temp + sensor->offset_constant - 8;
0145 
0146     /* reserve negative temperatures for errors */
0147     if (core_temp < 0)
0148         core_temp = 0;
0149 
0150     return core_temp;
0151 }
0152 
0153 static void
0154 nv50_therm_init(struct nvkm_therm *therm)
0155 {
0156     nv50_sensor_setup(therm);
0157 }
0158 
0159 static const struct nvkm_therm_func
0160 nv50_therm = {
0161     .init = nv50_therm_init,
0162     .intr = nv40_therm_intr,
0163     .pwm_ctrl = nv50_fan_pwm_ctrl,
0164     .pwm_get = nv50_fan_pwm_get,
0165     .pwm_set = nv50_fan_pwm_set,
0166     .pwm_clock = nv50_fan_pwm_clock,
0167     .temp_get = nv50_temp_get,
0168     .program_alarms = nvkm_therm_program_alarms_polling,
0169 };
0170 
0171 int
0172 nv50_therm_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst,
0173            struct nvkm_therm **ptherm)
0174 {
0175     return nvkm_therm_new_(&nv50_therm, device, type, inst, ptherm);
0176 }