/* * Factor by equations [2] and [3] from data sheet; valid for fans where the number of edges * equal (poles * 2 + 1).
*/ #define EMC2305_RPM_FACTOR 3932160
/** * struct emc2305_cdev_data - device-specific cooling device state * @cdev: cooling device * @cur_state: cooling current state * @last_hwmon_state: last cooling state updated by hwmon subsystem * @last_thermal_state: last cooling state updated by thermal subsystem * * The 'last_hwmon_state' and 'last_thermal_state' fields are provided to support fan low limit * speed feature. The purpose of this feature is to provides ability to limit fan speed * according to some system wise considerations, like absence of some replaceable units (PSU or * line cards), high system ambient temperature, unreliable transceivers temperature sensing or * some other factors which indirectly impacts system's airflow * Fan low limit feature is supported through 'hwmon' interface: 'hwmon' 'pwm' attribute is * used for setting low limit for fan speed in case 'thermal' subsystem is configured in * kernel. In this case setting fan speed through 'hwmon' will never let the 'thermal' * subsystem to select a lower duty cycle than the duty cycle selected with the 'pwm' * attribute. * From other side, fan speed is to be updated in hardware through 'pwm' only in case the * requested fan speed is above last speed set by 'thermal' subsystem, otherwise requested fan * speed will be just stored with no PWM update.
*/ struct emc2305_cdev_data { struct thermal_cooling_device *cdev; unsignedint cur_state; unsignedlong last_hwmon_state; unsignedlong last_thermal_state;
};
/** * struct emc2305_data - device-specific data * @client: i2c client * @hwmon_dev: hwmon device * @max_state: maximum cooling state of the cooling device * @pwm_num: number of PWM channels * @pwm_output_mask: PWM output mask * @pwm_polarity_mask: PWM polarity mask * @pwm_separate: separate PWM settings for every channel * @pwm_min: array of minimum PWM per channel * @pwm_freq: array of PWM frequency per channel * @cdev_data: array of cooling devices data
*/ struct emc2305_data { struct i2c_client *client; struct device *hwmon_dev;
u8 max_state;
u8 pwm_num;
u8 pwm_output_mask;
u8 pwm_polarity_mask; bool pwm_separate;
u8 pwm_min[EMC2305_PWM_MAX];
u16 pwm_freq[EMC2305_PWM_MAX]; struct emc2305_cdev_data cdev_data[EMC2305_PWM_MAX];
};
staticint emc2305_get_cdev_idx(struct thermal_cooling_device *cdev)
{ struct emc2305_data *data = cdev->devdata;
size_t len = strlen(cdev->type); int ret;
if (len <= 0) return -EINVAL;
/* * Returns index of cooling device 0..4 in case of separate PWM setting. * Zero index is used in case of one common PWM setting. * If the mode is not set as pwm_separate, all PWMs are to be bound * to the common thermal zone and should work at the same speed * to perform cooling for the same thermal junction. * Otherwise, return specific channel that will be used in bound * related PWM to the thermal zone.
*/ if (!data->pwm_separate) return 0;
staticint __emc2305_set_cur_state(struct emc2305_data *data, int cdev_idx, unsignedlong state)
{ int ret; struct i2c_client *client = data->client;
u8 val, i;
state = max_t(unsignedlong, state, data->cdev_data[cdev_idx].last_hwmon_state);
val = EMC2305_PWM_STATE2DUTY(state, data->max_state, EMC2305_FAN_MAX);
data->cdev_data[cdev_idx].cur_state = state; if (data->pwm_separate) {
ret = i2c_smbus_write_byte_data(client, EMC2305_REG_FAN_DRIVE(cdev_idx), val); if (ret < 0) return ret;
} else { /* * Set the same PWM value in all channels * if common PWM channel is used.
*/ for (i = 0; i < data->pwm_num; i++) {
ret = i2c_smbus_write_byte_data(client, EMC2305_REG_FAN_DRIVE(i), val); if (ret < 0) return ret;
}
}
if (IS_ERR(data->cdev_data[cdev_idx].cdev)) {
dev_err(dev, "Failed to register cooling device %s\n", emc2305_fan_name[idx]); return PTR_ERR(data->cdev_data[cdev_idx].cdev);
}
if (data->cdev_data[cdev_idx].cur_state > 0) /* Update pwm when temperature is above trips */
pwm = EMC2305_PWM_STATE2DUTY(data->cdev_data[cdev_idx].cur_state,
data->max_state, EMC2305_FAN_MAX);
/* Set minimal PWM speed. */ if (data->pwm_separate) {
ret = emc2305_set_pwm(dev, pwm, cdev_idx); if (ret < 0) return ret;
} else { for (i = 0; i < data->pwm_num; i++) {
ret = emc2305_set_pwm(dev, pwm, i); if (ret < 0) return ret;
}
}
data->cdev_data[cdev_idx].cur_state =
EMC2305_PWM_DUTY2STATE(pwm, data->max_state,
EMC2305_FAN_MAX);
data->cdev_data[cdev_idx].last_hwmon_state =
EMC2305_PWM_DUTY2STATE(pwm, data->max_state,
EMC2305_FAN_MAX); return 0;
}
staticint emc2305_set_tz(struct device *dev)
{ struct emc2305_data *data = dev_get_drvdata(dev); int i, ret;
if (!data->pwm_separate) return emc2305_set_single_tz(dev, dev->of_node, 0);
for (i = 0; i < data->pwm_num; i++) {
ret = emc2305_set_single_tz(dev, dev->of_node, i + 1); if (ret) return ret;
} return 0;
}
static umode_t
emc2305_is_visible(constvoid *data, enum hwmon_sensor_types type, u32 attr, int channel)
{ int max_channel = emc2305_get_max_channel(data);
/* Don't show channels which are not physically connected. */ if (channel >= max_channel) return 0; switch (type) { case hwmon_fan: switch (attr) { case hwmon_fan_input: return 0444; case hwmon_fan_fault: return 0444; default: break;
} break; case hwmon_pwm: switch (attr) { case hwmon_pwm_input: return 0644; default: break;
} break; default: break;
}
return 0;
};
staticint
emc2305_write(struct device *dev, enum hwmon_sensor_types type, u32 attr, int channel, long val)
{ struct emc2305_data *data = dev_get_drvdata(dev); int cdev_idx;
switch (type) { case hwmon_pwm: switch (attr) { case hwmon_pwm_input: /* If thermal is configured - handle PWM limit setting. */ if (IS_REACHABLE(CONFIG_THERMAL)) { if (data->pwm_separate)
cdev_idx = channel; else
cdev_idx = 0;
data->cdev_data[cdev_idx].last_hwmon_state =
EMC2305_PWM_DUTY2STATE(val, data->max_state,
EMC2305_FAN_MAX); /* * Update PWM only in case requested state is not less than the * last thermal state.
*/ if (data->cdev_data[cdev_idx].last_hwmon_state >=
data->cdev_data[cdev_idx].last_thermal_state) return __emc2305_set_cur_state(data, cdev_idx,
data->cdev_data[cdev_idx].last_hwmon_state); return 0;
} return emc2305_set_pwm(dev, val, channel); default: break;
} break; default: break;
}
return -EOPNOTSUPP;
};
staticint
emc2305_read(struct device *dev, enum hwmon_sensor_types type, u32 attr, int channel, long*val)
{ int ret;
switch (type) { case hwmon_fan: switch (attr) { case hwmon_fan_input:
ret = emc2305_show_fan(dev, channel); if (ret < 0) return ret;
*val = ret; return 0; case hwmon_fan_fault:
ret = emc2305_show_fault(dev, channel); if (ret < 0) return ret;
*val = ret; return 0; default: break;
} break; case hwmon_pwm: switch (attr) { case hwmon_pwm_input:
ret = emc2305_show_pwm(dev, channel); if (ret < 0) return ret;
*val = ret; return 0; default: break;
} break; default: break;
}
if (!pwm_childs) { if (pdata) { if (!pdata->max_state || pdata->max_state > EMC2305_FAN_MAX_STATE) return -EINVAL;
data->max_state = pdata->max_state; /* * Validate a number of active PWM channels. Note that * configured number can be less than the actual maximum * supported by the device.
*/ if (!pdata->pwm_num || pdata->pwm_num > EMC2305_PWM_MAX) return -EINVAL;
data->pwm_num = pdata->pwm_num;
data->pwm_output_mask = pdata->pwm_output_mask;
data->pwm_polarity_mask = pdata->pwm_polarity_mask;
data->pwm_separate = pdata->pwm_separate; for (i = 0; i < EMC2305_PWM_MAX; i++) {
data->pwm_min[i] = pdata->pwm_min[i];
data->pwm_freq[i] = pdata->pwm_freq[i];
}
} else {
data->max_state = EMC2305_FAN_MAX_STATE;
data->pwm_separate = false;
data->pwm_output_mask = EMC2305_DEFAULT_OUTPUT;
data->pwm_polarity_mask = EMC2305_DEFAULT_POLARITY; for (i = 0; i < EMC2305_PWM_MAX; i++) {
data->pwm_min[i] = EMC2305_FAN_MIN;
data->pwm_freq[i] = base_freq_table[3];
}
}
} else {
data->max_state = EMC2305_FAN_MAX_STATE;
data->pwm_separate = false; for (i = 0; i < EMC2305_PWM_MAX; i++)
data->pwm_min[i] = EMC2305_FAN_MIN;
}
if (IS_REACHABLE(CONFIG_THERMAL)) { /* Parse and check for the available PWM child nodes */ if (pwm_childs > 0) {
i = 0;
for_each_child_of_node(dev->of_node, child) {
ret = emc2305_set_single_tz(dev, child, i); if (ret != 0) return ret;
i++;
}
} else {
ret = emc2305_set_tz(dev); if (ret != 0) return ret;
}
}
ret = i2c_smbus_write_byte_data(client, EMC2305_REG_DRIVE_PWM_OUT,
data->pwm_output_mask); if (ret < 0)
dev_err(dev, "Failed to configure pwm output, using default\n");
ret = i2c_smbus_write_byte_data(client, EMC2305_REG_POLARITY,
data->pwm_polarity_mask); if (ret < 0)
dev_err(dev, "Failed to configure pwm polarity, using default\n");
for (i = 0; i < data->pwm_num; i++) {
ret = i2c_smbus_write_byte_data(client, EMC2305_REG_FAN_MIN_DRIVE(i),
data->pwm_min[i]); if (ret < 0) return ret;
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.