/* * pwm_fan.c fan driver that is controlled by pwm * * Copyright (c) 2013-2021, NVIDIA CORPORATION. All rights reserved. * * Author: Anshul Jain * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, * version 2, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "thermal_core.h" #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 14, 0) #include #endif #define USEC_PER_MIN (60L * USEC_PER_SEC) /* Based off of max device tree node name length */ #define MAX_PROFILE_NAME_LENGTH 31 bool is_fan_always_on(struct thermal_cooling_device *cdev) { struct fan_dev_data *fan_data = cdev->devdata; return fan_data->is_always_on; } EXPORT_SYMBOL(is_fan_always_on); static int fan_get_rpm_from_pwm(struct fan_dev_data *fan_data, unsigned int pwm) { int i; int y; for (i = 0; i < fan_data->active_steps; i++) { if (pwm == fan_data->fan_pwm[i]) return fan_data->fan_rpm[i]; } /* * The execution comes here only when an intermittent pwm value is * requested. The separate loop for index calculation optimizes * step-wise governor. */ for (i = 1; i < fan_data->active_steps; i++) { /* PWM table can be reverse in case of Tmargin. */ if (((fan_data->fan_pwm[i - 1] < pwm) && (pwm < fan_data->fan_pwm[i])) || ((fan_data->fan_pwm[i - 1] > pwm) && (pwm > fan_data->fan_pwm[i]))) break; } /* If not an active state, use linear extrapolation y = mx + c */ y = (((fan_data->fan_rpm[i] - fan_data->fan_rpm[i - 1]) * (((int)pwm) - fan_data->fan_pwm[i - 1])) / (fan_data->fan_pwm[i] - fan_data->fan_pwm[i - 1])) + fan_data->fan_rpm[i - 1]; dev_dbg(fan_data->dev, "Requested: pwm - %d, rpm - %d\n", pwm, y); return y; } /* Validate the current rpm with target rpm and update the pwm offset. */ static void fan_validate_target_rpm(struct fan_dev_data *fan_data) { if (!fan_data->pwm_tach_dev) fan_data->pwm_tach_dev = pwm_get_tach_dev(); if (!fan_data->pwm_tach_dev) { dev_err(fan_data->dev, "Failed to get tach device\n"); return; } fan_data->next_target_rpm = fan_get_rpm_from_pwm(fan_data, fan_data->next_target_pwm); fan_data->fan_rpm_in_limits = false; cancel_delayed_work_sync(&fan_data->fan_ramp_rpm_work); queue_delayed_work(fan_data->workqueue, &fan_data->fan_ramp_rpm_work, msecs_to_jiffies(fan_data->fan_ramp_time_ms)); } static void fan_update_target_pwm(struct fan_dev_data *fan_data, int val) { if (fan_data) { fan_data->next_target_pwm = min(val, fan_data->fan_cap_pwm); if (fan_data->next_target_pwm != fan_data->fan_cur_pwm) { cancel_delayed_work_sync(&fan_data->fan_ramp_pwm_work); queue_delayed_work(fan_data->workqueue, &fan_data->fan_ramp_pwm_work, msecs_to_jiffies(fan_data->step_time)); /* If dt node enabled, validate the rpm */ if (fan_data->use_tach_feedback) fan_validate_target_rpm(fan_data); } } } static ssize_t fan_target_pwm_show(struct device *dev, struct device_attribute *attr, char *buf) { struct fan_dev_data *fan_data = dev_get_drvdata(dev); int ret; if (!fan_data) return -EINVAL; mutex_lock(&fan_data->fan_state_lock); ret = sprintf(buf, "%d\n", fan_data->next_target_pwm); mutex_unlock(&fan_data->fan_state_lock); return ret; } static ssize_t fan_target_pwm_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct fan_dev_data *fan_data = dev_get_drvdata(dev); int ret, target_pwm = -1; ret = sscanf(buf, "%d", &target_pwm); if ((ret <= 0) || (!fan_data) || (target_pwm < 0)) return -EINVAL; mutex_lock(&fan_data->fan_state_lock); if (target_pwm > fan_data->fan_cap_pwm) target_pwm = fan_data->fan_cap_pwm; fan_update_target_pwm(fan_data, target_pwm); mutex_unlock(&fan_data->fan_state_lock); return count; } static ssize_t fan_temp_control_show(struct device *dev, struct device_attribute *attr, char *buf) { struct fan_dev_data *fan_data = dev_get_drvdata(dev); int ret; if (!fan_data) return -EINVAL; mutex_lock(&fan_data->fan_state_lock); ret = sprintf(buf, "%d\n", fan_data->fan_temp_control_flag); mutex_unlock(&fan_data->fan_state_lock); return ret; } static ssize_t fan_temp_control_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct fan_dev_data *fan_data = dev_get_drvdata(dev); int ret, fan_temp_control_flag = 0; ret = sscanf(buf, "%d", &fan_temp_control_flag); if ((ret <= 0) || (!fan_data)) return -EINVAL; mutex_lock(&fan_data->fan_state_lock); fan_data->fan_temp_control_flag = (fan_temp_control_flag > 0) ? 1 : 0; mutex_unlock(&fan_data->fan_state_lock); return count; } static ssize_t fan_tach_enabled_show(struct device *dev, struct device_attribute *attr, char *buf) { struct fan_dev_data *fan_data = dev_get_drvdata(dev); int ret, val; if (!fan_data) return -EINVAL; val = atomic_read(&fan_data->tach_enabled); ret = sprintf(buf, "%d\n", val); return ret; } static ssize_t fan_tach_enabled_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct fan_dev_data *fan_data = dev_get_drvdata(dev); int ret, val, tach_enabled; ret = kstrtoint(buf, 10, &val); if ((ret < 0) || (!fan_data)) return -EINVAL; if (!gpio_is_valid(fan_data->tach_gpio)) { dev_err(dev, "Can't find tach_gpio\n"); return -EPERM; } tach_enabled = atomic_read(&fan_data->tach_enabled); if (val == 1) { if (tach_enabled) { dev_err(dev, "tach irq is already enabled\n"); return -EINVAL; } if (!fan_data->tach_workqueue) { dev_err(dev, "tach not initialized\n"); return -EAGAIN; } fan_data->irq_count = 0; enable_irq(fan_data->tach_irq); atomic_set(&fan_data->tach_enabled, 1); queue_delayed_work(fan_data->tach_workqueue, &(fan_data->fan_tach_work), msecs_to_jiffies(fan_data->tach_period)); } else if (val == 0) { if (!tach_enabled) { dev_err(dev, "tach irq is already disabled\n"); return -EINVAL; } cancel_delayed_work(&fan_data->fan_tach_work); disable_irq(fan_data->tach_irq); atomic_set(&fan_data->tach_enabled, 0); atomic64_set(&fan_data->rpm_measured, 0); } else return -EINVAL; return count; } static ssize_t fan_pwm_cap_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct fan_dev_data *fan_data = dev_get_drvdata(dev); int val, ret, target_pwm; ret = sscanf(buf, "%d", &val); if ((ret <= 0) || (!fan_data)) return -EINVAL; if (val < 0) val = 0; mutex_lock(&fan_data->fan_state_lock); if (val > fan_data->fan_pwm_max) val = fan_data->fan_pwm_max; fan_data->fan_cap_pwm = val; target_pwm = min(fan_data->fan_cap_pwm, fan_data->next_target_pwm); fan_update_target_pwm(fan_data, target_pwm); mutex_unlock(&fan_data->fan_state_lock); return count; } static ssize_t fan_pwm_cap_show(struct device *dev, struct device_attribute *attr, char *buf) { struct fan_dev_data *fan_data = dev_get_drvdata(dev); int ret; if (!fan_data) return -EINVAL; mutex_lock(&fan_data->fan_state_lock); ret = sprintf(buf, "%d\n", fan_data->fan_cap_pwm); mutex_unlock(&fan_data->fan_state_lock); return ret; } static ssize_t fan_step_time_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct fan_dev_data *fan_data = dev_get_drvdata(dev); int ret, step_time = 0; ret = sscanf(buf, "%d", &step_time); if ((ret <= 0) || (!fan_data)) return -EINVAL; mutex_lock(&fan_data->fan_state_lock); fan_data->step_time = step_time; mutex_unlock(&fan_data->fan_state_lock); return count; } static ssize_t fan_cur_pwm_show(struct device *dev, struct device_attribute *attr, char *buf) { struct fan_dev_data *fan_data = dev_get_drvdata(dev); int ret; if (!fan_data) return -EINVAL; mutex_lock(&fan_data->fan_state_lock); ret = sprintf(buf, "%d\n", fan_data->fan_cur_pwm); mutex_unlock(&fan_data->fan_state_lock); return ret; } static ssize_t fan_step_time_show(struct device *dev, struct device_attribute *attr, char *buf) { struct fan_dev_data *fan_data = dev_get_drvdata(dev); int ret; if (!fan_data) return -EINVAL; mutex_lock(&fan_data->fan_state_lock); ret = sprintf(buf, "%d\n", fan_data->step_time); mutex_unlock(&fan_data->fan_state_lock); return ret; } static ssize_t fan_pwm_rpm_table_show(struct device *dev, struct device_attribute *attr, char *buf) { struct fan_dev_data *fan_data = dev_get_drvdata(dev); int i, bytes_written = 0; if (!fan_data) return -EINVAL; bytes_written = sprintf(buf + bytes_written, "%s\n", "(Index, RPM, PWM, RRU, RRD)"); mutex_lock(&fan_data->fan_state_lock); for (i = 0; i < fan_data->active_steps; ++i) { bytes_written += sprintf(buf + bytes_written, "(%d, %d, %d, %d, %d)\n", i, fan_data->fan_rpm[i], fan_data->fan_pwm[i], fan_data->fan_rru[i], fan_data->fan_rrd[i]); } mutex_unlock(&fan_data->fan_state_lock); return bytes_written; } /* * fan_rpm_measured_show - rpm_measured sysfs fops * Caller is responsible to wait at least "tach_period" msec after * enable "tach_enabled" to get right RPM value. */ static ssize_t fan_rpm_measured_show(struct device *dev, struct device_attribute *attr, char *buf) { struct fan_dev_data *fan_data = dev_get_drvdata(dev); int ret; u64 val; if (!fan_data) return -EINVAL; val = atomic64_read(&fan_data->rpm_measured); ret = sprintf(buf, "%lld\n", val); return ret; } static ssize_t fan_rpm_in_limit_show(struct device *dev, struct device_attribute *attr, char *buf) { struct fan_dev_data *fan_data = dev_get_drvdata(dev); int ret; if (!fan_data) return -EINVAL; mutex_lock(&fan_data->fan_state_lock); ret = sprintf(buf, "%s\n", fan_data->fan_rpm_in_limits ? "true" : "false"); mutex_unlock(&fan_data->fan_state_lock); return ret; } static int pwm_fan_get_cur_state(struct thermal_cooling_device *cdev, unsigned long *cur_state) { struct fan_dev_data *fan_data = cdev->devdata; if (!fan_data) return -EINVAL; mutex_lock(&fan_data->fan_state_lock); *cur_state = fan_data->next_state; mutex_unlock(&fan_data->fan_state_lock); return 0; } static int pwm_fan_set_cur_state(struct thermal_cooling_device *cdev, unsigned long cur_state) { struct fan_dev_data *fan_data = cdev->devdata; int target_pwm = 0; if (!fan_data) return -EINVAL; mutex_lock(&fan_data->fan_state_lock); if (!fan_data->fan_temp_control_flag) { mutex_unlock(&fan_data->fan_state_lock); return 0; } if (fan_data->continuous_gov) { /*"continuous_therm_gov" used, "cur_state" indicate target pwm value*/ target_pwm = min(fan_data->fan_cap_pwm, (int)cur_state); fan_data->next_target_pwm = target_pwm; fan_update_target_pwm(fan_data, target_pwm); mutex_unlock(&fan_data->fan_state_lock); return 0; } if (cur_state >= fan_data->active_steps) { mutex_unlock(&fan_data->fan_state_lock); return -EINVAL; } fan_data->next_state = cur_state; if ((fan_data->next_state < 0) || (fan_data->fan_pwm[fan_data->next_state] == 0)) { target_pwm = 0; if (fan_data->kickstart_en && delayed_work_pending(&fan_data->fan_hyst_work)) { cancel_delayed_work(&fan_data->fan_hyst_work); fan_data->fan_kickstart = false; } } else if (!fan_data->fan_cur_pwm && fan_data->kickstart_en) { if (fan_data->fan_kickstart) { mutex_unlock(&fan_data->fan_state_lock); return 0; } fan_data->fan_kickstart = true; target_pwm = fan_data->fan_startup_pwm; queue_delayed_work(fan_data->workqueue, &fan_data->fan_hyst_work, msecs_to_jiffies(fan_data->fan_startup_time + fan_data->step_time)); } else target_pwm = fan_data->fan_pwm[cur_state]; target_pwm = min(fan_data->fan_cap_pwm, target_pwm); fan_update_target_pwm(fan_data, target_pwm); mutex_unlock(&fan_data->fan_state_lock); return 0; } static int pwm_fan_get_max_state(struct thermal_cooling_device *cdev, unsigned long *max_state) { struct fan_dev_data *fan_data = cdev->devdata; *max_state = fan_data->active_steps; return 0; } static struct thermal_cooling_device_ops pwm_fan_cooling_ops = { .get_max_state = pwm_fan_get_max_state, .get_cur_state = pwm_fan_get_cur_state, .set_cur_state = pwm_fan_set_cur_state, }; static int fan_get_rpm_rru(int rpm, struct fan_dev_data *fan_data) { int rpm_delta = fan_data->next_target_rpm - rpm; int pwm_delta = (fan_data->fan_cur_pwm * rpm_delta) / fan_data->next_target_rpm; if (fan_data->is_tmargin) { while ((fan_data->fan_rpm_ramp_index > 1) && (rpm < fan_data->fan_rpm[fan_data->fan_rpm_ramp_index - 1])) fan_data->fan_rpm_ramp_index--; } else { while ((fan_data->fan_rpm_ramp_index < fan_data->active_steps - 1) && (rpm > fan_data->fan_rpm[fan_data->fan_rpm_ramp_index + 1])) fan_data->fan_rpm_ramp_index++; } return min(pwm_delta, fan_data->fan_rru[fan_data->fan_rpm_ramp_index]); } static int fan_get_rpm_rrd(int rpm, struct fan_dev_data *fan_data) { int rpm_delta = rpm - fan_data->next_target_rpm; int pwm_delta = (fan_data->fan_cur_pwm * rpm_delta) / fan_data->next_target_rpm; if (fan_data->is_tmargin) { while ((fan_data->fan_rpm_ramp_index < fan_data->active_steps) && (rpm < fan_data->fan_rpm[fan_data->fan_rpm_ramp_index + 1])) fan_data->fan_rpm_ramp_index++; } else { while ((fan_data->fan_rpm_ramp_index > 1) && (rpm < fan_data->fan_rpm[fan_data->fan_rpm_ramp_index - 1])) fan_data->fan_rpm_ramp_index--; } return min(pwm_delta, fan_data->fan_rru[fan_data->fan_rpm_ramp_index]); } static int fan_get_pwm_rru(int pwm, struct fan_dev_data *fan_data) { int i; if (fan_data->is_tmargin) { for (i = fan_data->active_steps - 1; i > 0; i--) { if ((fan_data->fan_pwm[i] <= pwm) && (pwm < fan_data->fan_pwm[i - 1])) return fan_data->fan_rru[i - 1]; } return fan_data->fan_rru[0]; } else { for (i = 0; i < fan_data->active_steps - 1 ; i++) { if ((fan_data->fan_pwm[i] <= pwm) && (pwm < fan_data->fan_pwm[i + 1])) return fan_data->fan_rru[i]; } return fan_data->fan_rru[fan_data->active_steps - 1]; } } static int fan_get_pwm_rrd(int pwm, struct fan_dev_data *fan_data) { int i; if (fan_data->is_tmargin) { for (i = fan_data->active_steps - 1; i > 0; i--) { if ((fan_data->fan_pwm[i] <= pwm) && (pwm < fan_data->fan_pwm[i - 1])) return fan_data->fan_rrd[i - 1]; } return fan_data->fan_rrd[0]; } else { for (i = 0; i < fan_data->active_steps - 1 ; i++) { if ((fan_data->fan_pwm[i] <= pwm) && (pwm < fan_data->fan_pwm[i + 1])) return fan_data->fan_rrd[i]; } return fan_data->fan_rrd[fan_data->active_steps - 1]; } } static void set_pwm_duty_cycle(int pwm, struct fan_dev_data *fan_data) { int duty; if (fan_data != NULL && fan_data->pwm_dev != NULL) { if (pwm == 0) { if (fan_data->fan_pwm_polarity == PWM_POLARITY_INVERSED) duty = fan_data->pwm_period; else duty = 0; } else { if (fan_data->fan_pwm_polarity == PWM_POLARITY_INVERSED) duty = fan_data->fan_pwm_max - pwm; else duty = pwm; duty = duty * fan_data->precision_multiplier / MULTIQP; } pwm_config(fan_data->pwm_dev, duty, fan_data->pwm_period); pwm_enable(fan_data->pwm_dev); } else { pr_err("FAN:PWM device or fan data is null\n"); } } static int get_next_higher_pwm(int pwm, struct fan_dev_data *fan_data) { int i; if (fan_data->continuous_gov) return fan_data->next_target_pwm; for (i = 0; i < fan_data->active_steps; i++) if (pwm < fan_data->fan_pwm[i]) return fan_data->fan_pwm[i]; return fan_data->fan_pwm[fan_data->active_steps - 1]; } static int get_next_lower_pwm(int pwm, struct fan_dev_data *fan_data) { int i; if (fan_data->continuous_gov) return fan_data->next_target_pwm; for (i = fan_data->active_steps - 1; i >= 0; i--) if (pwm > fan_data->fan_pwm[i]) return fan_data->fan_pwm[i]; return fan_data->fan_pwm[0]; } /* * @brief Validate the fan rpm with the expected target rpm. * * The fan rpm read from tach device is expected to be equal to target rpm * provided in DT. This validation check is expected to pass for 16 times for * the work thread to complete execution. * In case the difference between the read rpm and target rpm is more/less than * the given threshold value, the fan_rru/fan_rrd data should be added or * subtracted respectively from the current pwm value. In this case, the counter * restarts from 0 to 16. */ static void fan_ramping_rpm_work_func(struct work_struct *work) { int err, ret; int running_rpm = 0; int rrd, rru; int time_off; struct delayed_work *dwork = container_of(work, struct delayed_work, work); struct fan_dev_data *fan_data = container_of(dwork, struct fan_dev_data, fan_ramp_rpm_work); time_off = fan_data->rpm_invalid_retry_delay; if (!wait_for_completion_timeout(&fan_data->pwm_set, msecs_to_jiffies(10000))) { dev_info(fan_data->dev, "pwm_set completion timedout\n"); return; } if (!mutex_trylock(&fan_data->fan_state_lock)) { queue_delayed_work(fan_data->workqueue, &fan_data->fan_ramp_rpm_work, msecs_to_jiffies(fan_data->step_time + time_off)); return; } if (!fan_data->fan_temp_control_flag) goto unlock_mutex; ret = pwm_tach_capture_rpm(fan_data->pwm_tach_dev); if (ret < 0) { dev_dbg(fan_data->dev, "Failed to get rpm\n"); goto unlock_mutex; } else { running_rpm = ret; } if (abs(running_rpm - fan_data->next_target_rpm) < fan_data->rpm_diff_tolerance) { fan_data->fan_rpm_target_hit_count++; if (fan_data->fan_rpm_target_hit_count < fan_data->rpm_valid_retry_count) { time_off = fan_data->rpm_valid_retry_delay; goto reschedule; } dev_dbg(fan_data->dev, "Fan rpm in limits\n"); dev_dbg(fan_data->dev, "cur_rpm - %d, target_rpm - %d, cur_pwm - %d\n", running_rpm, fan_data->next_target_rpm, fan_data->fan_cur_pwm); fan_data->fan_rpm_in_limits = true; fan_data->fan_rpm_target_hit_count = 0; goto unlock_mutex; } dev_dbg(fan_data->dev, "Rpm diff crossed threshold\n"); dev_dbg(fan_data->dev, "cur_rpm - %d, target_rpm - %d, cur_pwm - %d\n", running_rpm, fan_data->next_target_rpm, fan_data->fan_cur_pwm); fan_data->fan_rpm_target_hit_count = 0; if (fan_data->next_target_rpm == 0) { if (fan_data->is_fan_reg_enabled) { fan_data->fan_cur_pwm = 0; err = regulator_disable(fan_data->fan_reg); if (err < 0) dev_err(fan_data->dev, " Coudn't disable vdd-fan\n"); else { dev_info(fan_data->dev, " Disabled vdd-fan\n"); fan_data->is_fan_reg_enabled = false; fan_data->fan_rpm_ramp_index = 0; } } fan_data->fan_rpm_in_limits = true; goto unlock_mutex; } if (!fan_data->is_fan_reg_enabled) { err = regulator_enable(fan_data->fan_reg); if (err < 0) { dev_err(fan_data->dev, " Coudn't enable vdd-fan\n"); goto unlock_mutex; } dev_info(fan_data->dev, " Enabled vdd-fan\n"); fan_data->is_fan_reg_enabled = true; fan_data->fan_cur_pwm = fan_data->fan_rru[0]; fan_data->fan_rpm_ramp_index = 1; } if (running_rpm > fan_data->next_target_rpm) { rrd = fan_get_rpm_rrd(running_rpm, fan_data); if (rrd == 0) { dev_dbg(fan_data->dev, " fan rrd value is 0\n"); goto unlock_mutex; } dev_dbg(fan_data->dev, "Subtracting rrd - %d from current pwm - %d\n", rrd, fan_data->fan_cur_pwm); fan_data->fan_cur_pwm -= rrd; fan_data->fan_cur_pwm = max(0, fan_data->fan_cur_pwm); /* No point in subtracting feedback offset if pwm is min */ if (fan_data->fan_cur_pwm == 0) { dev_dbg(fan_data->dev, "pwm at lower limit 0\n"); goto unlock_mutex; } } else { rru = fan_get_rpm_rru(running_rpm, fan_data); if (rru == 0) { dev_dbg(fan_data->dev, " fan rru value is 0\n"); goto unlock_mutex; } dev_dbg(fan_data->dev, "Adding rru - %d to current pwm - %d\n", rru, fan_data->fan_cur_pwm); fan_data->fan_cur_pwm += rru; fan_data->fan_cur_pwm = min(fan_data->fan_pwm_max, fan_data->fan_cur_pwm); /* No point in adding feedback offset if pwm is max */ if (fan_data->fan_cur_pwm == fan_data->fan_pwm_max) { dev_dbg(fan_data->dev, "pwm at upper limit %d\n", fan_data->fan_pwm_max); goto unlock_mutex; } } set_pwm_duty_cycle(fan_data->fan_cur_pwm, fan_data); time_off = fan_data->rpm_invalid_retry_delay; reschedule: queue_delayed_work(fan_data->workqueue, &fan_data->fan_ramp_rpm_work, msecs_to_jiffies(fan_data->step_time + time_off)); unlock_mutex: mutex_unlock(&fan_data->fan_state_lock); } static void fan_ramping_pwm_work_func(struct work_struct *work) { int rru, rrd, err; int cur_pwm, next_pwm; int time_off; struct delayed_work *dwork = container_of(work, struct delayed_work, work); struct fan_dev_data *fan_data = container_of(dwork, struct fan_dev_data, fan_ramp_pwm_work); time_off = fan_data->rpm_invalid_retry_delay; if (!mutex_trylock(&fan_data->fan_state_lock)) { if (fan_data->pwm_tach_dev) cancel_delayed_work_sync(&fan_data->fan_ramp_rpm_work); queue_delayed_work(fan_data->workqueue, &(fan_data->fan_ramp_pwm_work), msecs_to_jiffies(fan_data->step_time)); if (fan_data->pwm_tach_dev) queue_delayed_work(fan_data->workqueue, &fan_data->fan_ramp_rpm_work, msecs_to_jiffies(fan_data->step_time + time_off)); return; } reinit_completion(&fan_data->pwm_set); cur_pwm = fan_data->fan_cur_pwm; rru = fan_get_pwm_rru(cur_pwm, fan_data); rrd = fan_get_pwm_rrd(cur_pwm, fan_data); next_pwm = cur_pwm; if (fan_data->next_target_pwm > fan_data->fan_cur_pwm) { fan_data->fan_cur_pwm = fan_data->fan_cur_pwm + rru; next_pwm = min( get_next_higher_pwm(cur_pwm, fan_data), fan_data->fan_cur_pwm); next_pwm = min(fan_data->next_target_pwm, next_pwm); next_pwm = min(fan_data->fan_cap_pwm, next_pwm); } else if (fan_data->next_target_pwm < fan_data->fan_cur_pwm) { fan_data->fan_cur_pwm = fan_data->fan_cur_pwm - rrd; next_pwm = max(get_next_lower_pwm(cur_pwm, fan_data), fan_data->fan_cur_pwm); next_pwm = max(next_pwm, fan_data->next_target_pwm); next_pwm = max(0, next_pwm); } if ((next_pwm != 0) && !(fan_data->is_fan_reg_enabled)) { err = regulator_enable(fan_data->fan_reg); if (err < 0) dev_err(fan_data->dev, " Coudn't enable vdd-fan\n"); else { dev_dbg(fan_data->dev, " Enabled vdd-fan\n"); fan_data->is_fan_reg_enabled = true; } } if ((next_pwm == 0) && (fan_data->is_fan_reg_enabled)) { err = regulator_disable(fan_data->fan_reg); if (err < 0) dev_err(fan_data->dev, " Couldn't disable vdd-fan\n"); else { dev_dbg(fan_data->dev, " Disabled vdd-fan\n"); fan_data->is_fan_reg_enabled = false; } } set_pwm_duty_cycle(next_pwm, fan_data); fan_data->fan_cur_pwm = next_pwm; if (fan_data->next_target_pwm != next_pwm) { if (fan_data->pwm_tach_dev) cancel_delayed_work_sync(&fan_data->fan_ramp_rpm_work); queue_delayed_work(fan_data->workqueue, &(fan_data->fan_ramp_pwm_work), msecs_to_jiffies(fan_data->step_time)); if (fan_data->pwm_tach_dev) queue_delayed_work(fan_data->workqueue, &fan_data->fan_ramp_rpm_work, msecs_to_jiffies(fan_data->step_time + time_off)); } else complete(&fan_data->pwm_set); mutex_unlock(&fan_data->fan_state_lock); } static void fan_hyst_work_func(struct work_struct *work) { int target_pwm; struct delayed_work *dwork = container_of(work, struct delayed_work, work); struct fan_dev_data *fan_data = container_of(dwork, struct fan_dev_data, fan_hyst_work); mutex_lock(&fan_data->fan_state_lock); if (fan_data->fan_kickstart) { fan_data->fan_kickstart = false; target_pwm = fan_data->fan_pwm[fan_data->next_state]; target_pwm = min(fan_data->fan_cap_pwm, target_pwm); fan_update_target_pwm(fan_data, target_pwm); } mutex_unlock(&fan_data->fan_state_lock); } static long long time_diff_us(u64 start_ns, u64 end_ns) { return (end_ns - start_ns) / 1000; } static void fan_tach_work_func(struct work_struct *work) { struct delayed_work *dwork = to_delayed_work(work); struct fan_dev_data *fan_data = container_of(dwork, struct fan_dev_data, fan_tach_work); int tach_pulse_count, avg; long long diff_us = 0; unsigned long flags; spin_lock_irqsave(&fan_data->irq_lock, flags); tach_pulse_count = fan_data->irq_count; fan_data->irq_count = 0; diff_us = time_diff_us(fan_data->first_irq, fan_data->last_irq); spin_unlock_irqrestore(&fan_data->irq_lock, flags); /* get time diff */ if (tach_pulse_count <= 1) { atomic64_set(&fan_data->rpm_measured, 0); } else if (diff_us < 0) { dev_err(fan_data->dev, "invalid irq time diff: caught %d, diff_us %lld\n", tach_pulse_count, diff_us); } else { avg = diff_us / (tach_pulse_count - 1); /* 2 tach falling irq per round */ atomic64_set(&fan_data->rpm_measured, USEC_PER_MIN / (avg * 2)); } queue_delayed_work(fan_data->tach_workqueue, &(fan_data->fan_tach_work), msecs_to_jiffies(fan_data->tach_period)); } static ssize_t fan_profile_show(struct device *dev, struct device_attribute *attr, char *buf) { struct fan_dev_data *fan_data = dev_get_drvdata(dev); int ret; if (!fan_data) return -EINVAL; if (fan_data->num_profiles > 0) { ret = sprintf(buf, "%s\n", fan_data->fan_profile_names[fan_data->current_profile]); } else { ret = sprintf(buf, "N/A\n"); } return ret; } static ssize_t fan_available_profiles_show(struct device *dev, struct device_attribute *attr, char *buf) { struct fan_dev_data *fan_data = dev_get_drvdata(dev); int i; ssize_t count = 0; if (!fan_data) return -EINVAL; if (fan_data->num_profiles > 0) { for (i = 0; i < fan_data->num_profiles; ++i) { count += sprintf(&buf[count], "%s ", fan_data->fan_profile_names[i]); } /* Truncate the trailing space */ if (count) count--; count += sprintf(&buf[count], "\n"); } else { count = sprintf(buf, "N/A\n"); } return count; } static ssize_t fan_profile_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct fan_dev_data *fan_data = dev_get_drvdata(dev); size_t buf_len; int profile_index = -1; int i; if (!fan_data) return -EINVAL; buf_len = min(count, (size_t) MAX_PROFILE_NAME_LENGTH); while (buf_len > 0 && (buf[buf_len - 1] == '\n' || buf[buf_len - 1] == ' ')) buf_len--; if (buf_len == 0) return -EINVAL; for (i = 0; i < fan_data->num_profiles; ++i) { if (!strncmp(fan_data->fan_profile_names[i], buf, max(buf_len, strlen(fan_data->fan_profile_names[i])))) { profile_index = i; } } if (profile_index < 0) return -EINVAL; mutex_lock(&fan_data->fan_state_lock); memcpy(fan_data->fan_pwm, fan_data->fan_profile_pwms[profile_index], sizeof(int) * (fan_data->active_steps)); fan_data->fan_state_cap = fan_data->fan_profile_caps[profile_index]; fan_data->fan_cap_pwm = fan_data->fan_pwm[fan_data->fan_state_cap]; fan_data->current_profile = profile_index; mutex_unlock(&fan_data->fan_state_lock); return count; } /*State cap sysfs fops*/ static ssize_t fan_state_cap_show(struct device *dev, struct device_attribute *attr, char *buf) { struct fan_dev_data *fan_data = dev_get_drvdata(dev); int ret; if (!fan_data) return -EINVAL; mutex_lock(&fan_data->fan_state_lock); ret = sprintf(buf, "%d\n", fan_data->fan_state_cap); mutex_unlock(&fan_data->fan_state_lock); return ret; } static ssize_t fan_state_cap_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct fan_dev_data *fan_data = dev_get_drvdata(dev); int val, ret, target_pwm; ret = sscanf(buf, "%d", &val); if ((ret <= 0) || (!fan_data)) { dev_err(dev, "%s, fan_data is null or wrong input\n", __func__); return -EINVAL; } if (val < 0) val = 0; mutex_lock(&fan_data->fan_state_lock); if (val >= fan_data->active_steps) val = fan_data->active_steps - 1; fan_data->fan_state_cap = val; fan_data->fan_cap_pwm = fan_data->fan_pwm[fan_data->fan_state_cap_lookup[val]]; target_pwm = min(fan_data->fan_cap_pwm, fan_data->next_target_pwm); fan_update_target_pwm(fan_data, target_pwm); mutex_unlock(&fan_data->fan_state_lock); return count; } static ssize_t fan_pwm_state_map_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct fan_dev_data *fan_data = dev_get_drvdata(dev); int ret, index, pwm_val, target_pwm; ret = sscanf(buf, "%d %d", &index, &pwm_val); if ((ret <= 0) || (!fan_data) || (index < 0) || (pwm_val < 0)) return -EINVAL; dev_dbg(dev, "index=%d, pwm_val=%d", index, pwm_val); mutex_lock(&fan_data->fan_state_lock); if ((pwm_val > fan_data->fan_cap_pwm) || (index > fan_data->active_steps)) { mutex_unlock(&fan_data->fan_state_lock); return -EINVAL; } fan_data->fan_pwm[index] = pwm_val; if (index == fan_data->next_state) { if (fan_data->next_target_pwm != fan_data->fan_pwm[index]) { target_pwm = fan_data->fan_pwm[index]; target_pwm = min(fan_data->fan_cap_pwm, target_pwm); fan_update_target_pwm(fan_data, target_pwm); } } mutex_unlock(&fan_data->fan_state_lock); return count; } static ssize_t kickstart_params_show(struct device *dev, struct device_attribute *attr, char *buf) { struct fan_dev_data *fan_data = dev_get_drvdata(dev); int bytes_written = 0; if (!fan_data) return -EINVAL; mutex_lock(&fan_data->fan_state_lock); bytes_written += sprintf(buf, "pwm:%d, time:%dms\n", fan_data->fan_startup_pwm, fan_data->fan_startup_time); mutex_unlock(&fan_data->fan_state_lock); return bytes_written; } static ssize_t kickstart_params_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct fan_dev_data *fan_data = dev_get_drvdata(dev); int ret, fan_startup_pwm, fan_startup_time; ret = sscanf(buf, "%d %d", &fan_startup_pwm, &fan_startup_time); if ((ret < 2) || (!fan_data) || (fan_startup_pwm < 0) || (fan_startup_time < 0)) return -EINVAL; dev_dbg(dev, "fan_startup_pwm=%d, fan_startup_time=%d\n", fan_startup_pwm, fan_startup_time); mutex_lock(&fan_data->fan_state_lock); fan_data->fan_startup_pwm = min(fan_startup_pwm, fan_data->fan_cap_pwm); fan_data->fan_startup_time = fan_startup_time; if (fan_data->fan_startup_pwm && fan_data->fan_startup_time) fan_data->kickstart_en = true; else fan_data->kickstart_en = false; mutex_unlock(&fan_data->fan_state_lock); return count; } static ssize_t fan_kickstart_show(struct device *dev, struct device_attribute *attr, char *buf) { struct fan_dev_data *fan_data = dev_get_drvdata(dev); int bytes_written = 0; if (!fan_data) return -EINVAL; mutex_lock(&fan_data->fan_state_lock); bytes_written += sprintf(buf + bytes_written, "%d\n", fan_data->fan_kickstart); mutex_unlock(&fan_data->fan_state_lock); return bytes_written; } static void kickstart_fan(struct fan_dev_data *fan_data) { int target_pwm; if (!fan_data->fan_kickstart) return; target_pwm = min(fan_data->fan_cap_pwm, fan_data->fan_startup_pwm); fan_update_target_pwm(fan_data, target_pwm); queue_delayed_work(fan_data->workqueue, &fan_data->fan_hyst_work, msecs_to_jiffies(fan_data->fan_startup_time)); } static ssize_t fan_kickstart_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct fan_dev_data *fan_data = dev_get_drvdata(dev); int ret, fan_kickstart = 0; ret = sscanf(buf, "%d", &fan_kickstart); if ((ret <= 0) || (!fan_data) || (fan_kickstart < 0) || (fan_data->fan_startup_pwm <= 0) || (fan_data->fan_startup_time <= 0)) return -EINVAL; dev_dbg(dev, "fan_kickstart=%d", fan_kickstart); mutex_lock(&fan_data->fan_state_lock); fan_data->fan_kickstart = !!fan_kickstart; if (fan_kickstart) kickstart_fan(fan_data); mutex_unlock(&fan_data->fan_state_lock); return count; } static DEVICE_ATTR(pwm_cap, S_IWUSR | S_IRUGO, fan_pwm_cap_show, fan_pwm_cap_store); static DEVICE_ATTR(state_cap, S_IWUSR | S_IRUGO, fan_state_cap_show, fan_state_cap_store); static DEVICE_ATTR(pwm_state_map, S_IWUSR | S_IRUGO, NULL, fan_pwm_state_map_store); static DEVICE_ATTR(cur_pwm, S_IRUGO, fan_cur_pwm_show, NULL); static DEVICE_ATTR(target_pwm, S_IWUSR | S_IRUGO, fan_target_pwm_show, fan_target_pwm_store); static DEVICE_ATTR(tach_enable, S_IWUSR | S_IRUGO, fan_tach_enabled_show, fan_tach_enabled_store); static DEVICE_ATTR(rpm_measured, S_IRUGO, fan_rpm_measured_show, NULL); static DEVICE_ATTR(temp_control, S_IWUSR | S_IRUGO, fan_temp_control_show, fan_temp_control_store); static DEVICE_ATTR(step_time, S_IWUSR | S_IRUGO, fan_step_time_show, fan_step_time_store); static DEVICE_ATTR(pwm_rpm_table, S_IRUGO, fan_pwm_rpm_table_show, NULL); static DEVICE_ATTR(fan_profile, S_IWUSR | S_IRUGO, fan_profile_show, fan_profile_store); static DEVICE_ATTR(fan_available_profiles, S_IWUSR | S_IRUGO, fan_available_profiles_show, NULL); static DEVICE_ATTR(kickstart_params, S_IWUSR | S_IRUGO, kickstart_params_show, kickstart_params_store); static DEVICE_ATTR(fan_kickstart, S_IWUSR | S_IRUGO, fan_kickstart_show, fan_kickstart_store); static DEVICE_ATTR(fan_rpm_in_limit, S_IRUGO, fan_rpm_in_limit_show, NULL); static struct attribute *pwm_fan_attrs[] = { &dev_attr_fan_profile.attr, &dev_attr_fan_available_profiles.attr, &dev_attr_pwm_cap.attr, &dev_attr_state_cap.attr, &dev_attr_pwm_state_map.attr, &dev_attr_cur_pwm.attr, &dev_attr_target_pwm.attr, &dev_attr_tach_enable.attr, &dev_attr_rpm_measured.attr, &dev_attr_temp_control.attr, &dev_attr_step_time.attr, &dev_attr_pwm_rpm_table.attr, &dev_attr_kickstart_params.attr, &dev_attr_fan_kickstart.attr, &dev_attr_fan_rpm_in_limit.attr, NULL }; ATTRIBUTE_GROUPS(pwm_fan); static int add_sysfs_entry(struct device *dev) { return sysfs_create_group(&dev->kobj, &pwm_fan_group); } static void remove_sysfs_entry(struct device *dev) { sysfs_remove_group(&dev->kobj, &pwm_fan_group); } irqreturn_t fan_tach_isr(int irq, void *data) { struct fan_dev_data *fan_data = data; u64 curr_time; long long diff_us = 0; unsigned long flags; if (!fan_data) return IRQ_HANDLED; spin_lock_irqsave(&fan_data->irq_lock, flags); curr_time = sched_clock(); if (fan_data->irq_count == 0) fan_data->first_irq = curr_time; else fan_data->last_irq = curr_time; if (fan_data->old_irq != 0) { diff_us = time_diff_us(fan_data->old_irq, curr_time); /* WAR: To skip double irq under 20usec and irq when rising */ if (diff_us > 20 && !gpio_get_value(fan_data->tach_gpio)) fan_data->irq_count++; } else fan_data->irq_count++; fan_data->old_irq = curr_time; spin_unlock_irqrestore(&fan_data->irq_lock, flags); return IRQ_HANDLED; } static int pwm_fan_probe(struct platform_device *pdev) { int i; struct fan_dev_data *fan_data = NULL; int *rpm_data; int *rru_data; int *rrd_data; int *lookup_data; int *pwm_data; int err = 0; int of_err = 0; struct device_node *node = NULL; struct device_node *data_node = NULL; struct device_node *base_profile_node = NULL; struct device_node *profile_node = NULL; const char *default_profile = NULL; const char *type = NULL; u32 value; int pwm_fan_gpio; int tach_gpio; int fan_startup_time = 0; int fan_startup_pwm = 0; struct device *hwmon; if (!pdev) return -EINVAL; node = pdev->dev.of_node; if (!node) { pr_err("FAN: dev of_node NULL\n"); return -EINVAL; } data_node = of_parse_phandle(node, "shared_data", 0); if (!data_node) { pr_err("PWM shared data node NULL, parse phandle failed\n"); return -EINVAL; } fan_data = devm_kzalloc(&pdev->dev, sizeof(struct fan_dev_data), GFP_KERNEL); if (!fan_data) return -ENOMEM; fan_data->dev = &pdev->dev; of_property_read_string(node, "regulator-name", &fan_data->regulator_name); fan_data->fan_reg = fan_data->regulator_name ? regulator_get(fan_data->dev, fan_data->regulator_name) : regulator_get(fan_data->dev, "vdd-fan"); if (IS_ERR_OR_NULL(fan_data->fan_reg)) { pr_err("FAN: coudln't get the regulator\n"); devm_kfree(&pdev->dev, (void *)fan_data); return -ENODEV; } fan_data->is_tmargin = of_property_read_bool(node, "use_tmargin"); of_err |= of_property_read_string(node, "name", &fan_data->name); pr_info("FAN dev name: %s\n", fan_data->name); pwm_fan_gpio = of_get_named_gpio(data_node, "pwm_gpio", 0); if (pwm_fan_gpio < 0) of_err |= pwm_fan_gpio; err = gpio_request(pwm_fan_gpio, "pwm-fan"); if (err < 0) { pr_err("FAN:gpio request failed\n"); err = -EINVAL; goto gpio_request_fail; } else { pr_info("FAN:gpio request success.\n"); } of_err |= of_property_read_u32(data_node, "active_steps", &value); fan_data->active_steps = (int)value; of_err |= of_property_read_u32(data_node, "pwm_period", &value); fan_data->pwm_period = (int)value; of_err |= of_property_read_u32(data_node, "pwm_id", &value); fan_data->pwm_id = (int)value; of_err |= of_property_read_u32(data_node, "step_time", &value); fan_data->step_time = (int)value; of_err |= of_property_read_u32(data_node, "active_pwm_max", &value); fan_data->fan_pwm_max = (int)value; of_err |= of_property_read_u32(data_node, "state_cap", &value); fan_data->fan_state_cap = (int)value; if (of_property_read_u32(data_node, "pwm_polarity", &value)) fan_data->fan_pwm_polarity = PWM_POLARITY_INVERSED; else if (value > PWM_POLARITY_INVERSED) { dev_warn(&pdev->dev, "invalid polarity, use inversed by default\n"); fan_data->fan_pwm_polarity = PWM_POLARITY_INVERSED; } else fan_data->fan_pwm_polarity = value; /* * suspend_state value is now expected from DT property but not all * platforms provide this propery via DT and expect suspend_state * to be 1. Hence keeping suspend_state to be 1 by default if * property is not provided by DT and function returns -EINVAL */ err = of_property_read_u32(data_node, "suspend_state", &value); if (err == -EINVAL) { value = 1; err = 0; } fan_data->suspend_state = !!value; fan_data->pwm_gpio = pwm_fan_gpio; if (of_err) { err = -ENXIO; goto rpm_alloc_fail; } tach_gpio = of_get_named_gpio(data_node, "tach_gpio", 0); if (tach_gpio < 0) { fan_data->tach_gpio = -1; pr_info("FAN: can't find tach_gpio\n"); } else { fan_data->tach_gpio = tach_gpio; value = tach_gpio; } /* fan pwm profiles */ fan_data->num_profiles = 0; base_profile_node = of_get_child_by_name(node, "profiles"); if (base_profile_node) { fan_data->num_profiles = of_get_available_child_count(base_profile_node); } if (fan_data->num_profiles > 0) { of_err |= of_property_read_string(base_profile_node, "default", &default_profile); dev_info(&pdev->dev, "Found %d profiles, default profile is %s\n", fan_data->num_profiles, default_profile); fan_data->fan_profile_names = devm_kzalloc(&pdev->dev, sizeof(const char*) * fan_data->num_profiles, GFP_KERNEL); if (!fan_data->fan_profile_names) { err = -ENOMEM; goto profile_alloc_fail; } fan_data->fan_profile_pwms = devm_kzalloc(&pdev->dev, sizeof(int*) * fan_data->num_profiles, GFP_KERNEL); if (!fan_data->fan_profile_pwms) { err = -ENOMEM; goto profile_alloc_fail; } fan_data->fan_profile_caps = devm_kzalloc(&pdev->dev, sizeof(int) * fan_data->num_profiles, GFP_KERNEL); if (!fan_data->fan_profile_caps) { err = -ENOMEM; goto profile_alloc_fail; } fan_data->current_profile = 0; i = 0; for_each_available_child_of_node (base_profile_node, profile_node) { of_err |= of_property_read_string(profile_node, "name", &fan_data->fan_profile_names[i]); if (default_profile && !strncmp(default_profile, fan_data->fan_profile_names[i], MAX_PROFILE_NAME_LENGTH)) { fan_data->current_profile = i; } value = 0; of_err |= of_property_read_u32(profile_node, "state_cap", &value); fan_data->fan_profile_caps[i] = (int) value; pwm_data = devm_kzalloc(&pdev->dev, sizeof(int) * (fan_data->active_steps), GFP_KERNEL); if (!pwm_data) { err = -ENOMEM; goto profile_alloc_fail; } of_err |= of_property_read_u32_array(profile_node, "active_pwm", pwm_data, (size_t) fan_data->active_steps); fan_data->fan_profile_pwms[i] = pwm_data; i++; } } /* rpm array */ rpm_data = devm_kzalloc(&pdev->dev, sizeof(int) * (fan_data->active_steps), GFP_KERNEL); if (!rpm_data) { err = -ENOMEM; goto rpm_alloc_fail; } of_err |= of_property_read_u32_array(data_node, "active_rpm", rpm_data, (size_t) fan_data->active_steps); fan_data->fan_rpm = rpm_data; /* rru array */ rru_data = devm_kzalloc(&pdev->dev, sizeof(int) * (fan_data->active_steps), GFP_KERNEL); if (!rru_data) { err = -ENOMEM; goto rru_alloc_fail; } of_err |= of_property_read_u32_array(data_node, "active_rru", rru_data, (size_t) fan_data->active_steps); fan_data->fan_rru = rru_data; /* rrd array */ rrd_data = devm_kzalloc(&pdev->dev, sizeof(int) * (fan_data->active_steps), GFP_KERNEL); if (!rrd_data) { err = -ENOMEM; goto rrd_alloc_fail; } of_err |= of_property_read_u32_array(data_node, "active_rrd", rrd_data, (size_t) fan_data->active_steps); fan_data->fan_rrd = rrd_data; /* state_cap_lookup array */ lookup_data = devm_kzalloc(&pdev->dev, sizeof(int) * (fan_data->active_steps), GFP_KERNEL); if (!lookup_data) { err = -ENOMEM; goto lookup_alloc_fail; } of_err |= of_property_read_u32_array(data_node, "state_cap_lookup", lookup_data, (size_t) fan_data->active_steps); fan_data->fan_state_cap_lookup = lookup_data; /* pwm array */ /* If profiles are in use, use current profile pwm */ pwm_data = devm_kzalloc(&pdev->dev, sizeof(int) * (fan_data->active_steps), GFP_KERNEL); if (!pwm_data) { err = -ENOMEM; goto pwm_alloc_fail; } if (fan_data->num_profiles > 0) { memcpy(pwm_data, fan_data->fan_profile_pwms[fan_data->current_profile], sizeof(int) * (fan_data->active_steps)); } else { of_err |= of_property_read_u32_array(node, "active_pwm", pwm_data, (size_t) fan_data->active_steps); } fan_data->fan_pwm = pwm_data; if (of_err) { err = -ENXIO; goto workqueue_alloc_fail; } mutex_init(&fan_data->fan_state_lock); init_completion(&fan_data->pwm_set); spin_lock_init(&fan_data->irq_lock); fan_data->workqueue = alloc_workqueue(dev_name(&pdev->dev), WQ_HIGHPRI | WQ_UNBOUND, 1); if (!fan_data->workqueue) { err = -ENOMEM; goto workqueue_alloc_fail; } /* Use profile cap if profiles are in use*/ if (fan_data->num_profiles > 0) { fan_data->fan_state_cap = fan_data->fan_profile_caps[fan_data->current_profile]; } fan_data->fan_cap_pwm = fan_data->fan_pwm[fan_data->fan_state_cap]; fan_data->precision_multiplier = (fan_data->pwm_period * MULTIQP) / fan_data->fan_pwm_max; dev_info(&pdev->dev, "cap state:%d, cap pwm:%d\n", fan_data->fan_state_cap, fan_data->fan_cap_pwm); if (of_find_property(node, "fan_startup_pwm", NULL) || of_find_property(node, "fan_startup_pwm", NULL)) { of_err = of_property_read_u32(node, "fan_startup_pwm", &fan_startup_pwm); fan_data->fan_startup_pwm = min(fan_startup_pwm, fan_data->fan_cap_pwm); of_err |= of_property_read_u32(node, "fan_startup_time", &fan_startup_time); fan_data->fan_startup_time = (int)fan_startup_time; if (of_err || !fan_data->fan_startup_pwm || !fan_data->fan_startup_time) { fan_data->kickstart_en = false; dev_err(&pdev->dev, "fan_startup init fail, fail to enable kick start feature\n"); err = -ENOMEM; goto workqueue_alloc_fail; } else { fan_data->kickstart_en = true; } } fan_data->fan_kickstart = false; fan_data->continuous_gov = of_property_read_bool(node, "continuous_gov_boot_on"); fan_data->is_always_on = of_property_read_bool(node, "is_always_on"); INIT_DELAYED_WORK(&(fan_data->fan_ramp_pwm_work), fan_ramping_pwm_work_func); fan_data->pwm_tach_dev = NULL; fan_data->use_tach_feedback = of_property_read_bool(node, "use_tach_feedback"); if (fan_data->use_tach_feedback) { of_err = of_property_read_u32(node, "rpm_ramp_time_ms", &value); if (of_err) { dev_err(&pdev->dev, "Failed to get fan ramp time"); err = -EINVAL; goto tach_feedback_fail; } else { fan_data->fan_ramp_time_ms = value; } of_err = of_property_read_u32(node, "rpm_diff_tolerance", &value); if (!of_err) { fan_data->rpm_diff_tolerance = (int)value; dev_info(&pdev->dev, "Using tachometer rpm feedback control"); INIT_DELAYED_WORK( &(fan_data->fan_ramp_rpm_work), fan_ramping_rpm_work_func); } else { dev_err(&pdev->dev, "Failed to get rpm difference tolerance value"); err = -EINVAL; goto tach_feedback_fail; } of_err |= of_property_read_u32(node, "rpm_valid_retry_delay", &value); if (!of_err) { fan_data->rpm_valid_retry_delay = value; } else { dev_err(&pdev->dev, "Failed to get rpm valid retry delay"); goto tach_feedback_fail; } of_err |= of_property_read_u32(node, "rpm_invalid_retry_delay", &value); if (!of_err) { fan_data->rpm_invalid_retry_delay = value; } else { dev_err(&pdev->dev, "Failed to get rpm invalid retry delay"); goto tach_feedback_fail; } of_err = of_property_read_u32(node, "rpm_valid_retry_count", &value); if (!of_err) { fan_data->rpm_valid_retry_count = value; } else { dev_err(&pdev->dev, "Failed to get rpm valid retry count"); goto tach_feedback_fail; } } INIT_DELAYED_WORK(&(fan_data->fan_hyst_work), fan_hyst_work_func); type = of_get_property(node, "cdev-type", NULL); if (type == NULL) type = "pwm-fan"; fan_data->cdev = thermal_of_cooling_device_register(node, (char *) type, fan_data, &pwm_fan_cooling_ops); if (IS_ERR_OR_NULL(fan_data->cdev)) { dev_err(&pdev->dev, "Failed to register cooling device\n"); err = -EINVAL; goto cdev_register_fail; } fan_data->pwm_dev = devm_pwm_get(&pdev->dev, NULL); if (IS_ERR(fan_data->pwm_dev) && PTR_ERR(fan_data->pwm_dev) != -EPROBE_DEFER) { dev_err(&pdev->dev, "unable to request PWM, trying legacy API\n"); fan_data->pwm_legacy_api = true; fan_data->pwm_dev = pwm_request(fan_data->pwm_id, dev_name(&pdev->dev)); } if (IS_ERR(fan_data->pwm_dev)) { err = PTR_ERR(fan_data->pwm_dev); if (err != -EPROBE_DEFER) dev_err(&pdev->dev, "unable to request PWM for fan\n"); goto pwm_req_fail; } else { dev_info(&pdev->dev, "got pwm for fan. polarity is %s\n", fan_data->fan_pwm_polarity ? "inversed" : "normal"); } atomic_set(&fan_data->tach_enabled, 0); if (fan_data->tach_gpio != -1) { /* init fan tach */ fan_data->tach_irq = gpio_to_irq(fan_data->tach_gpio); err = gpio_request(fan_data->tach_gpio, "pwm-fan-tach"); if (err < 0) { dev_err(&pdev->dev, "fan tach gpio request failed\n"); goto tach_gpio_request_fail; } err = gpio_direction_input(fan_data->tach_gpio); if (err < 0) { dev_err(&pdev->dev, "fan tach set gpio direction input failed\n"); goto tach_request_irq_fail; } /* TODO: Find a workaround for using the deprecated * gpio_sysfs_set_active_low API which has been deprecated on * the 4.4 kernel version. The calls to change the poliarity of * the tach_gpio has been temporarily disabled as it is not * needed for regular operation of the fan cooling device. */ err = devm_request_threaded_irq(&pdev->dev, fan_data->tach_irq, NULL, fan_tach_isr, IRQF_TRIGGER_FALLING | IRQF_ONESHOT, "pwm-fan-tach", fan_data); if (err < 0) { dev_err(&pdev->dev, "fan request irq failed\n"); goto tach_request_irq_fail; } dev_info(&pdev->dev, "fan tach request irq success\n"); disable_irq_nosync(fan_data->tach_irq); } of_err = of_property_read_u32(data_node, "tach_period", &value); if (of_err < 0) dev_err(&pdev->dev, "parsing tach_period error: %d\n", of_err); else { fan_data->tach_period = (int) value; dev_info(&pdev->dev, "tach period: %d\n", fan_data->tach_period); /* init tach work related */ fan_data->tach_workqueue = alloc_workqueue(fan_data->name, WQ_UNBOUND, 1); if (!fan_data->tach_workqueue) { err = -ENOMEM; goto tach_workqueue_alloc_fail; } INIT_DELAYED_WORK(&(fan_data->fan_tach_work), fan_tach_work_func); } /* init rpm related values */ fan_data->irq_count = 0; atomic64_set(&fan_data->rpm_measured, 0); /*turn temp control on*/ fan_data->fan_temp_control_flag = 1; set_pwm_duty_cycle(fan_data->fan_pwm[0], fan_data); fan_data->fan_cur_pwm = fan_data->fan_pwm[0]; gpio_free(fan_data->pwm_gpio); platform_set_drvdata(pdev, fan_data); if (add_sysfs_entry(&pdev->dev)) { dev_err(&pdev->dev, "FAN:Can't create syfs node"); err = -ENOMEM; goto sysfs_fail; } /* print out initialized info */ for (i = 0; i < fan_data->active_steps; i++) { dev_info(&pdev->dev, "index %d: pwm=%d, rpm=%d, rru=%d, rrd=%d, state:%d\n", i, fan_data->fan_pwm[i], fan_data->fan_rpm[i], fan_data->fan_rru[i], fan_data->fan_rrd[i], fan_data->fan_state_cap_lookup[i]); } hwmon = devm_hwmon_device_register_with_groups(&pdev->dev, "tegra_pwmfan", fan_data, pwm_fan_groups); if (IS_ERR(hwmon)) { dev_err(&pdev->dev, "Failed to register hwmon device\n"); pwm_disable(fan_data->pwm_dev); err = PTR_ERR(hwmon); goto hwmon_fail; } return err; hwmon_fail: remove_sysfs_entry(&pdev->dev); sysfs_fail: destroy_workqueue(fan_data->tach_workqueue); tach_workqueue_alloc_fail: tach_request_irq_fail: tach_gpio_request_fail: if (fan_data->pwm_legacy_api) pwm_free(fan_data->pwm_dev); pwm_req_fail: thermal_cooling_device_unregister(fan_data->cdev); cdev_register_fail: destroy_workqueue(fan_data->workqueue); workqueue_alloc_fail: tach_feedback_fail: profile_alloc_fail: pwm_alloc_fail: lookup_alloc_fail: rrd_alloc_fail: rru_alloc_fail: rpm_alloc_fail: gpio_free(pwm_fan_gpio); gpio_request_fail: if (err == -ENXIO) pr_err("FAN: of_property_read failed\n"); else if (err == -ENOMEM) pr_err("FAN: memery allocation failed\n"); return err; } static int pwm_fan_remove(struct platform_device *pdev) { struct fan_dev_data *fan_data = platform_get_drvdata(pdev); if (!fan_data) return -EINVAL; thermal_cooling_device_unregister(fan_data->cdev); remove_sysfs_entry(&pdev->dev); cancel_delayed_work(&fan_data->fan_tach_work); destroy_workqueue(fan_data->tach_workqueue); disable_irq(fan_data->tach_irq); if (fan_data->tach_gpio != -1) gpio_free(fan_data->tach_gpio); if (fan_data->kickstart_en) { cancel_delayed_work_sync(&fan_data->fan_hyst_work); fan_data->fan_kickstart = false; } cancel_delayed_work(&fan_data->fan_ramp_pwm_work); if (fan_data->pwm_tach_dev) cancel_delayed_work(&fan_data->fan_ramp_rpm_work); destroy_workqueue(fan_data->workqueue); pwm_config(fan_data->pwm_dev, 0, fan_data->pwm_period); pwm_disable(fan_data->pwm_dev); if (fan_data->pwm_legacy_api) pwm_free(fan_data->pwm_dev); if (fan_data->fan_reg) regulator_put(fan_data->fan_reg); return 0; } #if CONFIG_PM static int pwm_fan_suspend(struct platform_device *pdev, pm_message_t state) { struct fan_dev_data *fan_data = platform_get_drvdata(pdev); int err; mutex_lock(&fan_data->fan_state_lock); if (fan_data->kickstart_en) { cancel_delayed_work(&fan_data->fan_hyst_work); fan_data->fan_kickstart = false; } cancel_delayed_work(&fan_data->fan_ramp_pwm_work); if (fan_data->pwm_tach_dev) cancel_delayed_work(&fan_data->fan_ramp_rpm_work); set_pwm_duty_cycle(0, fan_data); pwm_disable(fan_data->pwm_dev); err = gpio_request(fan_data->pwm_gpio, "pwm-fan"); if (err < 0) { dev_err(&pdev->dev, "%s:gpio request failed %d\n", __func__, fan_data->pwm_gpio); } gpio_direction_output(fan_data->pwm_gpio, fan_data->suspend_state); if (fan_data->is_fan_reg_enabled) { err = regulator_disable(fan_data->fan_reg); if (err < 0) dev_err(&pdev->dev, "Not able to disable Fan regulator\n"); else { dev_dbg(fan_data->dev, " Disabled vdd-fan\n"); fan_data->is_fan_reg_enabled = false; } } /*Stop thermal control*/ fan_data->fan_temp_control_flag = 0; mutex_unlock(&fan_data->fan_state_lock); return 0; } static int pwm_fan_resume(struct platform_device *pdev) { struct fan_dev_data *fan_data = platform_get_drvdata(pdev); gpio_free(fan_data->pwm_gpio); reinit_completion(&fan_data->pwm_set); queue_delayed_work(fan_data->workqueue, &fan_data->fan_ramp_pwm_work, msecs_to_jiffies(fan_data->step_time)); if (fan_data->pwm_tach_dev) queue_delayed_work(fan_data->workqueue, &fan_data->fan_ramp_rpm_work, msecs_to_jiffies(fan_data->step_time)); fan_data->fan_temp_control_flag = 1; return 0; } #endif static const struct of_device_id of_pwm_fan_match[] = { { .compatible = "loki-pwm-fan", }, { .compatible = "ers-pwm-fan", }, { .compatible = "foster-pwm-fan", }, { .compatible = "pwm-fan", }, {}, }; MODULE_DEVICE_TABLE(of, of_pwm_fan_match); static struct platform_driver pwm_fan_driver = { .driver = { .name = "pwm_fan_driver", .owner = THIS_MODULE, .of_match_table = of_pwm_fan_match, }, .probe = pwm_fan_probe, .remove = pwm_fan_remove, #if CONFIG_PM .suspend = pwm_fan_suspend, .resume = pwm_fan_resume, #endif }; module_platform_driver(pwm_fan_driver); MODULE_DESCRIPTION("pwm fan driver"); MODULE_AUTHOR("Anshul Jain "); MODULE_LICENSE("GPL v2");