Beispiel #1
0
int
nouveau_therm_fan_get(struct nouveau_therm *therm)
{
	struct nouveau_therm_priv *priv = (void *)therm;
	struct nouveau_gpio *gpio = nouveau_gpio(therm);
	struct dcb_gpio_func func;
	int card_type = nv_device(therm)->card_type;
	u32 divs, duty;
	int ret;

	if (!priv->fan.pwm_get)
		return -ENODEV;

	ret = gpio->find(gpio, 0, DCB_GPIO_PWM_FAN, 0xff, &func);
	if (ret == 0) {
		ret = priv->fan.pwm_get(therm, func.line, &divs, &duty);
		if (ret == 0 && divs) {
			divs = max(divs, duty);
			if (card_type <= NV_40 || (func.log[0] & 1))
				duty = divs - duty;
			return (duty * 100) / divs;
		}

		return gpio->get(gpio, 0, func.func, func.line) * 100;
	}

	return -ENODEV;
}
Beispiel #2
0
int
nouveau_therm_fan_ctor(struct nouveau_therm *therm)
{
	struct nouveau_therm_priv *priv = (void *)therm;
	struct nouveau_gpio *gpio = nouveau_gpio(therm);
	struct nouveau_bios *bios = nouveau_bios(therm);
	struct dcb_gpio_func func;
	int ret;

	/* attempt to locate a drivable fan, and determine control method */
	ret = gpio->find(gpio, 0, DCB_GPIO_FAN, 0xff, &func);
	if (ret == 0) {
		/* FIXME: is this really the place to perform such checks ? */
		if (func.line != 16 && func.log[0] & DCB_GPIO_LOG_DIR_IN) {
			nv_debug(therm, "GPIO_FAN is in input mode\n");
			ret = -EINVAL;
		} else {
			ret = nouveau_fanpwm_create(therm, &func);
			if (ret != 0)
				ret = nouveau_fantog_create(therm, &func);
		}
	}

	/* no controllable fan found, create a dummy fan module */
	if (ret != 0) {
		ret = nouveau_fannil_create(therm);
		if (ret)
			return ret;
	}

	nv_info(therm, "FAN control: %s\n", priv->fan->type);

	/* read the current speed, it is useful when resuming */
	priv->fan->percent = nouveau_therm_fan_get(therm);

	/* attempt to detect a tachometer connection */
	ret = gpio->find(gpio, 0, DCB_GPIO_FAN_SENSE, 0xff, &priv->fan->tach);
	if (ret)
		priv->fan->tach.func = DCB_GPIO_UNUSED;

	/* initialise fan bump/slow update handling */
	priv->fan->parent = therm;
	nouveau_alarm_init(&priv->fan->alarm, nouveau_fan_alarm);
	spin_lock_init(&priv->fan->lock);

	/* other random init... */
	nouveau_therm_fan_set_defaults(therm);
	nvbios_perf_fan_parse(bios, &priv->fan->perf);
	if (!nvbios_fan_parse(bios, &priv->fan->bios)) {
		nv_debug(therm, "parsing the fan table failed\n");
		if (nvbios_therm_fan_parse(bios, &priv->fan->bios))
			nv_error(therm, "parsing both fan tables failed\n");
	}
	nouveau_therm_fan_safety_checks(therm);
	return 0;
}
Beispiel #3
0
static int
nouveau_fanpwm_get(struct nouveau_therm *therm)
{
	struct nouveau_therm_priv *tpriv = (void *)therm;
	struct nouveau_fanpwm_priv *priv = (void *)tpriv->fan;
	struct nouveau_gpio *gpio = nouveau_gpio(therm);
	int card_type = nv_device(therm)->card_type;
	u32 divs, duty;
	int ret;

	ret = therm->pwm_get(therm, priv->func.line, &divs, &duty);
	if (ret == 0 && divs) {
		divs = max(divs, duty);
		if (card_type <= NV_40 || (priv->func.log[0] & 1))
			duty = divs - duty;
		return (duty * 100) / divs;
	}

	return gpio->get(gpio, 0, priv->func.func, priv->func.line) * 100;
}
Beispiel #4
0
int
nouveau_therm_fan_set(struct nouveau_therm *therm, int percent)
{
	struct nouveau_therm_priv *priv = (void *)therm;
	struct nouveau_gpio *gpio = nouveau_gpio(therm);
	struct dcb_gpio_func func;
	int card_type = nv_device(therm)->card_type;
	u32 divs, duty;
	int ret;

	if (priv->fan.mode == FAN_CONTROL_NONE)
		return -EINVAL;

	if (!priv->fan.pwm_set)
		return -ENODEV;

	if (percent < priv->bios_fan.min_duty)
		percent = priv->bios_fan.min_duty;
	if (percent > priv->bios_fan.max_duty)
		percent = priv->bios_fan.max_duty;

	ret = gpio->find(gpio, 0, DCB_GPIO_PWM_FAN, 0xff, &func);
	if (ret == 0) {
		divs = priv->bios_perf_fan.pwm_divisor;
		if (priv->bios_fan.pwm_freq) {
			divs = 1;
			if (priv->fan.pwm_clock)
				divs = priv->fan.pwm_clock(therm);
			divs /= priv->bios_fan.pwm_freq;
		}

		duty = ((divs * percent) + 99) / 100;
		if (card_type <= NV_40 || (func.log[0] & 1))
			duty = divs - duty;

		ret = priv->fan.pwm_set(therm, func.line, divs, duty);
		return ret;
	}

	return -ENODEV;
}
Beispiel #5
0
int
nouveau_therm_fan_sense(struct nouveau_therm *therm)
{
	struct nouveau_therm_priv *priv = (void *)therm;
	struct nouveau_timer *ptimer = nouveau_timer(therm);
	struct nouveau_gpio *gpio = nouveau_gpio(therm);
	u32 cycles, cur, prev;
	u64 start, end, tach;

	if (priv->fan->tach.func == DCB_GPIO_UNUSED)
		return -ENODEV;

	/* Time a complete rotation and extrapolate to RPM:
	 * When the fan spins, it changes the value of GPIO FAN_SENSE.
	 * We get 4 changes (0 -> 1 -> 0 -> 1) per complete rotation.
	 */
	start = ptimer->read(ptimer);
	prev = gpio->get(gpio, 0, priv->fan->tach.func, priv->fan->tach.line);
	cycles = 0;
	do {
		usleep_range(500, 1000); /* supports 0 < rpm < 7500 */

		cur = gpio->get(gpio, 0, priv->fan->tach.func, priv->fan->tach.line);
		if (prev != cur) {
			if (!start)
				start = ptimer->read(ptimer);
			cycles++;
			prev = cur;
		}
	} while (cycles < 5 && ptimer->read(ptimer) - start < 250000000);
	end = ptimer->read(ptimer);

	if (cycles == 5) {
		tach = (u64)60000000000ULL;
		do_div(tach, (end - start));
		return tach;
	} else
		return 0;
}