static ssize_t set_pwm()

in dme1737.c [1259:1456]


static ssize_t set_pwm(struct device *dev, struct device_attribute *attr,
		       const char *buf, size_t count)
{
	struct dme1737_data *data = dev_get_drvdata(dev);
	struct sensor_device_attribute_2
		*sensor_attr_2 = to_sensor_dev_attr_2(attr);
	int ix = sensor_attr_2->index;
	int fn = sensor_attr_2->nr;
	long val;
	int err;

	err = kstrtol(buf, 10, &val);
	if (err)
		return err;

	mutex_lock(&data->update_lock);
	switch (fn) {
	case SYS_PWM:
		data->pwm[ix] = clamp_val(val, 0, 255);
		dme1737_write(data, DME1737_REG_PWM(ix), data->pwm[ix]);
		break;
	case SYS_PWM_FREQ:
		data->pwm_freq[ix] = PWM_FREQ_TO_REG(val, dme1737_read(data,
						DME1737_REG_PWM_FREQ(ix)));
		dme1737_write(data, DME1737_REG_PWM_FREQ(ix),
			      data->pwm_freq[ix]);
		break;
	case SYS_PWM_ENABLE:
		/* Only valid for pwm[1-3] */
		if (val < 0 || val > 2) {
			count = -EINVAL;
			dev_warn(dev,
				 "PWM enable %ld not supported. Choose one of 0, 1, or 2.\n",
				 val);
			goto exit;
		}
		/* Refresh the cache */
		data->pwm_config[ix] = dme1737_read(data,
						DME1737_REG_PWM_CONFIG(ix));
		if (val == PWM_EN_FROM_REG(data->pwm_config[ix])) {
			/* Bail out if no change */
			goto exit;
		}
		/* Do some housekeeping if we are currently in auto mode */
		if (PWM_EN_FROM_REG(data->pwm_config[ix]) == 2) {
			/* Save the current zone channel assignment */
			data->pwm_acz[ix] = PWM_ACZ_FROM_REG(
							data->pwm_config[ix]);
			/* Save the current ramp rate state and disable it */
			data->pwm_rr[ix > 0] = dme1737_read(data,
						DME1737_REG_PWM_RR(ix > 0));
			data->pwm_rr_en &= ~(1 << ix);
			if (PWM_RR_EN_FROM_REG(data->pwm_rr[ix > 0], ix)) {
				data->pwm_rr_en |= (1 << ix);
				data->pwm_rr[ix > 0] = PWM_RR_EN_TO_REG(0, ix,
							data->pwm_rr[ix > 0]);
				dme1737_write(data,
					      DME1737_REG_PWM_RR(ix > 0),
					      data->pwm_rr[ix > 0]);
			}
		}
		/* Set the new PWM mode */
		switch (val) {
		case 0:
			/* Change permissions of pwm[ix] to read-only */
			dme1737_chmod_file(dev, dme1737_pwm_chmod_attr[ix],
					   S_IRUGO);
			/* Turn fan fully on */
			data->pwm_config[ix] = PWM_EN_TO_REG(0,
							data->pwm_config[ix]);
			dme1737_write(data, DME1737_REG_PWM_CONFIG(ix),
				      data->pwm_config[ix]);
			break;
		case 1:
			/* Turn on manual mode */
			data->pwm_config[ix] = PWM_EN_TO_REG(1,
							data->pwm_config[ix]);
			dme1737_write(data, DME1737_REG_PWM_CONFIG(ix),
				      data->pwm_config[ix]);
			/* Change permissions of pwm[ix] to read-writeable */
			dme1737_chmod_file(dev, dme1737_pwm_chmod_attr[ix],
					   S_IRUGO | S_IWUSR);
			break;
		case 2:
			/* Change permissions of pwm[ix] to read-only */
			dme1737_chmod_file(dev, dme1737_pwm_chmod_attr[ix],
					   S_IRUGO);
			/*
			 * Turn on auto mode using the saved zone channel
			 * assignment
			 */
			data->pwm_config[ix] = PWM_ACZ_TO_REG(
							data->pwm_acz[ix],
							data->pwm_config[ix]);
			dme1737_write(data, DME1737_REG_PWM_CONFIG(ix),
				      data->pwm_config[ix]);
			/* Enable PWM ramp rate if previously enabled */
			if (data->pwm_rr_en & (1 << ix)) {
				data->pwm_rr[ix > 0] = PWM_RR_EN_TO_REG(1, ix,
						dme1737_read(data,
						DME1737_REG_PWM_RR(ix > 0)));
				dme1737_write(data,
					      DME1737_REG_PWM_RR(ix > 0),
					      data->pwm_rr[ix > 0]);
			}
			break;
		}
		break;
	case SYS_PWM_RAMP_RATE:
		/* Only valid for pwm[1-3] */
		/* Refresh the cache */
		data->pwm_config[ix] = dme1737_read(data,
						DME1737_REG_PWM_CONFIG(ix));
		data->pwm_rr[ix > 0] = dme1737_read(data,
						DME1737_REG_PWM_RR(ix > 0));
		/* Set the ramp rate value */
		if (val > 0) {
			data->pwm_rr[ix > 0] = PWM_RR_TO_REG(val, ix,
							data->pwm_rr[ix > 0]);
		}
		/*
		 * Enable/disable the feature only if the associated PWM
		 * output is in automatic mode.
		 */
		if (PWM_EN_FROM_REG(data->pwm_config[ix]) == 2) {
			data->pwm_rr[ix > 0] = PWM_RR_EN_TO_REG(val > 0, ix,
							data->pwm_rr[ix > 0]);
		}
		dme1737_write(data, DME1737_REG_PWM_RR(ix > 0),
			      data->pwm_rr[ix > 0]);
		break;
	case SYS_PWM_AUTO_CHANNELS_ZONE:
		/* Only valid for pwm[1-3] */
		if (!(val == 1 || val == 2 || val == 4 ||
		      val == 6 || val == 7)) {
			count = -EINVAL;
			dev_warn(dev,
				 "PWM auto channels zone %ld not supported. Choose one of 1, 2, 4, 6, "
				 "or 7.\n", val);
			goto exit;
		}
		/* Refresh the cache */
		data->pwm_config[ix] = dme1737_read(data,
						DME1737_REG_PWM_CONFIG(ix));
		if (PWM_EN_FROM_REG(data->pwm_config[ix]) == 2) {
			/*
			 * PWM is already in auto mode so update the temp
			 * channel assignment
			 */
			data->pwm_config[ix] = PWM_ACZ_TO_REG(val,
						data->pwm_config[ix]);
			dme1737_write(data, DME1737_REG_PWM_CONFIG(ix),
				      data->pwm_config[ix]);
		} else {
			/*
			 * PWM is not in auto mode so we save the temp
			 * channel assignment for later use
			 */
			data->pwm_acz[ix] = val;
		}
		break;
	case SYS_PWM_AUTO_PWM_MIN:
		/* Only valid for pwm[1-3] */
		/* Refresh the cache */
		data->pwm_min[ix] = dme1737_read(data,
						DME1737_REG_PWM_MIN(ix));
		/*
		 * There are only 2 values supported for the auto_pwm_min
		 * value: 0 or auto_point1_pwm. So if the temperature drops
		 * below the auto_point1_temp_hyst value, the fan either turns
		 * off or runs at auto_point1_pwm duty-cycle.
		 */
		if (val > ((data->pwm_min[ix] + 1) / 2)) {
			data->pwm_rr[0] = PWM_OFF_TO_REG(1, ix,
						dme1737_read(data,
						DME1737_REG_PWM_RR(0)));
		} else {
			data->pwm_rr[0] = PWM_OFF_TO_REG(0, ix,
						dme1737_read(data,
						DME1737_REG_PWM_RR(0)));
		}
		dme1737_write(data, DME1737_REG_PWM_RR(0),
			      data->pwm_rr[0]);
		break;
	case SYS_PWM_AUTO_POINT1_PWM:
		/* Only valid for pwm[1-3] */
		data->pwm_min[ix] = clamp_val(val, 0, 255);
		dme1737_write(data, DME1737_REG_PWM_MIN(ix),
			      data->pwm_min[ix]);
		break;
	default:
		dev_dbg(dev, "Unknown function %d.\n", fn);
	}
exit:
	mutex_unlock(&data->update_lock);

	return count;
}