18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * gpio-fan.c - Hwmon driver for fans connected to GPIO lines. 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2010 LaCie 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Author: Simon Guinot <sguinot@lacie.com> 88c2ecf20Sopenharmony_ci */ 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci#include <linux/module.h> 118c2ecf20Sopenharmony_ci#include <linux/init.h> 128c2ecf20Sopenharmony_ci#include <linux/slab.h> 138c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 148c2ecf20Sopenharmony_ci#include <linux/irq.h> 158c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 168c2ecf20Sopenharmony_ci#include <linux/err.h> 178c2ecf20Sopenharmony_ci#include <linux/mutex.h> 188c2ecf20Sopenharmony_ci#include <linux/hwmon.h> 198c2ecf20Sopenharmony_ci#include <linux/gpio/consumer.h> 208c2ecf20Sopenharmony_ci#include <linux/of.h> 218c2ecf20Sopenharmony_ci#include <linux/of_platform.h> 228c2ecf20Sopenharmony_ci#include <linux/thermal.h> 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_cistruct gpio_fan_speed { 258c2ecf20Sopenharmony_ci int rpm; 268c2ecf20Sopenharmony_ci int ctrl_val; 278c2ecf20Sopenharmony_ci}; 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_cistruct gpio_fan_data { 308c2ecf20Sopenharmony_ci struct device *dev; 318c2ecf20Sopenharmony_ci struct device *hwmon_dev; 328c2ecf20Sopenharmony_ci /* Cooling device if any */ 338c2ecf20Sopenharmony_ci struct thermal_cooling_device *cdev; 348c2ecf20Sopenharmony_ci struct mutex lock; /* lock GPIOs operations. */ 358c2ecf20Sopenharmony_ci int num_gpios; 368c2ecf20Sopenharmony_ci struct gpio_desc **gpios; 378c2ecf20Sopenharmony_ci int num_speed; 388c2ecf20Sopenharmony_ci struct gpio_fan_speed *speed; 398c2ecf20Sopenharmony_ci int speed_index; 408c2ecf20Sopenharmony_ci#ifdef CONFIG_PM_SLEEP 418c2ecf20Sopenharmony_ci int resume_speed; 428c2ecf20Sopenharmony_ci#endif 438c2ecf20Sopenharmony_ci bool pwm_enable; 448c2ecf20Sopenharmony_ci struct gpio_desc *alarm_gpio; 458c2ecf20Sopenharmony_ci struct work_struct alarm_work; 468c2ecf20Sopenharmony_ci}; 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_ci/* 498c2ecf20Sopenharmony_ci * Alarm GPIO. 508c2ecf20Sopenharmony_ci */ 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_cistatic void fan_alarm_notify(struct work_struct *ws) 538c2ecf20Sopenharmony_ci{ 548c2ecf20Sopenharmony_ci struct gpio_fan_data *fan_data = 558c2ecf20Sopenharmony_ci container_of(ws, struct gpio_fan_data, alarm_work); 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_ci sysfs_notify(&fan_data->hwmon_dev->kobj, NULL, "fan1_alarm"); 588c2ecf20Sopenharmony_ci kobject_uevent(&fan_data->hwmon_dev->kobj, KOBJ_CHANGE); 598c2ecf20Sopenharmony_ci} 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_cistatic irqreturn_t fan_alarm_irq_handler(int irq, void *dev_id) 628c2ecf20Sopenharmony_ci{ 638c2ecf20Sopenharmony_ci struct gpio_fan_data *fan_data = dev_id; 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_ci schedule_work(&fan_data->alarm_work); 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ci return IRQ_NONE; 688c2ecf20Sopenharmony_ci} 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_cistatic ssize_t fan1_alarm_show(struct device *dev, 718c2ecf20Sopenharmony_ci struct device_attribute *attr, char *buf) 728c2ecf20Sopenharmony_ci{ 738c2ecf20Sopenharmony_ci struct gpio_fan_data *fan_data = dev_get_drvdata(dev); 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ci return sprintf(buf, "%d\n", 768c2ecf20Sopenharmony_ci gpiod_get_value_cansleep(fan_data->alarm_gpio)); 778c2ecf20Sopenharmony_ci} 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(fan1_alarm); 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_cistatic int fan_alarm_init(struct gpio_fan_data *fan_data) 828c2ecf20Sopenharmony_ci{ 838c2ecf20Sopenharmony_ci int alarm_irq; 848c2ecf20Sopenharmony_ci struct device *dev = fan_data->dev; 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci /* 878c2ecf20Sopenharmony_ci * If the alarm GPIO don't support interrupts, just leave 888c2ecf20Sopenharmony_ci * without initializing the fail notification support. 898c2ecf20Sopenharmony_ci */ 908c2ecf20Sopenharmony_ci alarm_irq = gpiod_to_irq(fan_data->alarm_gpio); 918c2ecf20Sopenharmony_ci if (alarm_irq <= 0) 928c2ecf20Sopenharmony_ci return 0; 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ci INIT_WORK(&fan_data->alarm_work, fan_alarm_notify); 958c2ecf20Sopenharmony_ci irq_set_irq_type(alarm_irq, IRQ_TYPE_EDGE_BOTH); 968c2ecf20Sopenharmony_ci return devm_request_irq(dev, alarm_irq, fan_alarm_irq_handler, 978c2ecf20Sopenharmony_ci IRQF_SHARED, "GPIO fan alarm", fan_data); 988c2ecf20Sopenharmony_ci} 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci/* 1018c2ecf20Sopenharmony_ci * Control GPIOs. 1028c2ecf20Sopenharmony_ci */ 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_ci/* Must be called with fan_data->lock held, except during initialization. */ 1058c2ecf20Sopenharmony_cistatic void __set_fan_ctrl(struct gpio_fan_data *fan_data, int ctrl_val) 1068c2ecf20Sopenharmony_ci{ 1078c2ecf20Sopenharmony_ci int i; 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_ci for (i = 0; i < fan_data->num_gpios; i++) 1108c2ecf20Sopenharmony_ci gpiod_set_value_cansleep(fan_data->gpios[i], 1118c2ecf20Sopenharmony_ci (ctrl_val >> i) & 1); 1128c2ecf20Sopenharmony_ci} 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_cistatic int __get_fan_ctrl(struct gpio_fan_data *fan_data) 1158c2ecf20Sopenharmony_ci{ 1168c2ecf20Sopenharmony_ci int i; 1178c2ecf20Sopenharmony_ci int ctrl_val = 0; 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_ci for (i = 0; i < fan_data->num_gpios; i++) { 1208c2ecf20Sopenharmony_ci int value; 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_ci value = gpiod_get_value_cansleep(fan_data->gpios[i]); 1238c2ecf20Sopenharmony_ci ctrl_val |= (value << i); 1248c2ecf20Sopenharmony_ci } 1258c2ecf20Sopenharmony_ci return ctrl_val; 1268c2ecf20Sopenharmony_ci} 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci/* Must be called with fan_data->lock held, except during initialization. */ 1298c2ecf20Sopenharmony_cistatic void set_fan_speed(struct gpio_fan_data *fan_data, int speed_index) 1308c2ecf20Sopenharmony_ci{ 1318c2ecf20Sopenharmony_ci if (fan_data->speed_index == speed_index) 1328c2ecf20Sopenharmony_ci return; 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_ci __set_fan_ctrl(fan_data, fan_data->speed[speed_index].ctrl_val); 1358c2ecf20Sopenharmony_ci fan_data->speed_index = speed_index; 1368c2ecf20Sopenharmony_ci} 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_cistatic int get_fan_speed_index(struct gpio_fan_data *fan_data) 1398c2ecf20Sopenharmony_ci{ 1408c2ecf20Sopenharmony_ci int ctrl_val = __get_fan_ctrl(fan_data); 1418c2ecf20Sopenharmony_ci int i; 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ci for (i = 0; i < fan_data->num_speed; i++) 1448c2ecf20Sopenharmony_ci if (fan_data->speed[i].ctrl_val == ctrl_val) 1458c2ecf20Sopenharmony_ci return i; 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci dev_warn(fan_data->dev, 1488c2ecf20Sopenharmony_ci "missing speed array entry for GPIO value 0x%x\n", ctrl_val); 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ci return -ENODEV; 1518c2ecf20Sopenharmony_ci} 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_cistatic int rpm_to_speed_index(struct gpio_fan_data *fan_data, unsigned long rpm) 1548c2ecf20Sopenharmony_ci{ 1558c2ecf20Sopenharmony_ci struct gpio_fan_speed *speed = fan_data->speed; 1568c2ecf20Sopenharmony_ci int i; 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_ci for (i = 0; i < fan_data->num_speed; i++) 1598c2ecf20Sopenharmony_ci if (speed[i].rpm >= rpm) 1608c2ecf20Sopenharmony_ci return i; 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci return fan_data->num_speed - 1; 1638c2ecf20Sopenharmony_ci} 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_cistatic ssize_t pwm1_show(struct device *dev, struct device_attribute *attr, 1668c2ecf20Sopenharmony_ci char *buf) 1678c2ecf20Sopenharmony_ci{ 1688c2ecf20Sopenharmony_ci struct gpio_fan_data *fan_data = dev_get_drvdata(dev); 1698c2ecf20Sopenharmony_ci u8 pwm = fan_data->speed_index * 255 / (fan_data->num_speed - 1); 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ci return sprintf(buf, "%d\n", pwm); 1728c2ecf20Sopenharmony_ci} 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_cistatic ssize_t pwm1_store(struct device *dev, struct device_attribute *attr, 1758c2ecf20Sopenharmony_ci const char *buf, size_t count) 1768c2ecf20Sopenharmony_ci{ 1778c2ecf20Sopenharmony_ci struct gpio_fan_data *fan_data = dev_get_drvdata(dev); 1788c2ecf20Sopenharmony_ci unsigned long pwm; 1798c2ecf20Sopenharmony_ci int speed_index; 1808c2ecf20Sopenharmony_ci int ret = count; 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_ci if (kstrtoul(buf, 10, &pwm) || pwm > 255) 1838c2ecf20Sopenharmony_ci return -EINVAL; 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_ci mutex_lock(&fan_data->lock); 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_ci if (!fan_data->pwm_enable) { 1888c2ecf20Sopenharmony_ci ret = -EPERM; 1898c2ecf20Sopenharmony_ci goto exit_unlock; 1908c2ecf20Sopenharmony_ci } 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_ci speed_index = DIV_ROUND_UP(pwm * (fan_data->num_speed - 1), 255); 1938c2ecf20Sopenharmony_ci set_fan_speed(fan_data, speed_index); 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_ciexit_unlock: 1968c2ecf20Sopenharmony_ci mutex_unlock(&fan_data->lock); 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_ci return ret; 1998c2ecf20Sopenharmony_ci} 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_cistatic ssize_t pwm1_enable_show(struct device *dev, 2028c2ecf20Sopenharmony_ci struct device_attribute *attr, char *buf) 2038c2ecf20Sopenharmony_ci{ 2048c2ecf20Sopenharmony_ci struct gpio_fan_data *fan_data = dev_get_drvdata(dev); 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ci return sprintf(buf, "%d\n", fan_data->pwm_enable); 2078c2ecf20Sopenharmony_ci} 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_cistatic ssize_t pwm1_enable_store(struct device *dev, 2108c2ecf20Sopenharmony_ci struct device_attribute *attr, 2118c2ecf20Sopenharmony_ci const char *buf, size_t count) 2128c2ecf20Sopenharmony_ci{ 2138c2ecf20Sopenharmony_ci struct gpio_fan_data *fan_data = dev_get_drvdata(dev); 2148c2ecf20Sopenharmony_ci unsigned long val; 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_ci if (kstrtoul(buf, 10, &val) || val > 1) 2178c2ecf20Sopenharmony_ci return -EINVAL; 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_ci if (fan_data->pwm_enable == val) 2208c2ecf20Sopenharmony_ci return count; 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_ci mutex_lock(&fan_data->lock); 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_ci fan_data->pwm_enable = val; 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_ci /* Disable manual control mode: set fan at full speed. */ 2278c2ecf20Sopenharmony_ci if (val == 0) 2288c2ecf20Sopenharmony_ci set_fan_speed(fan_data, fan_data->num_speed - 1); 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_ci mutex_unlock(&fan_data->lock); 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_ci return count; 2338c2ecf20Sopenharmony_ci} 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_cistatic ssize_t pwm1_mode_show(struct device *dev, 2368c2ecf20Sopenharmony_ci struct device_attribute *attr, char *buf) 2378c2ecf20Sopenharmony_ci{ 2388c2ecf20Sopenharmony_ci return sprintf(buf, "0\n"); 2398c2ecf20Sopenharmony_ci} 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_cistatic ssize_t fan1_min_show(struct device *dev, 2428c2ecf20Sopenharmony_ci struct device_attribute *attr, char *buf) 2438c2ecf20Sopenharmony_ci{ 2448c2ecf20Sopenharmony_ci struct gpio_fan_data *fan_data = dev_get_drvdata(dev); 2458c2ecf20Sopenharmony_ci 2468c2ecf20Sopenharmony_ci return sprintf(buf, "%d\n", fan_data->speed[0].rpm); 2478c2ecf20Sopenharmony_ci} 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_cistatic ssize_t fan1_max_show(struct device *dev, 2508c2ecf20Sopenharmony_ci struct device_attribute *attr, char *buf) 2518c2ecf20Sopenharmony_ci{ 2528c2ecf20Sopenharmony_ci struct gpio_fan_data *fan_data = dev_get_drvdata(dev); 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_ci return sprintf(buf, "%d\n", 2558c2ecf20Sopenharmony_ci fan_data->speed[fan_data->num_speed - 1].rpm); 2568c2ecf20Sopenharmony_ci} 2578c2ecf20Sopenharmony_ci 2588c2ecf20Sopenharmony_cistatic ssize_t fan1_input_show(struct device *dev, 2598c2ecf20Sopenharmony_ci struct device_attribute *attr, char *buf) 2608c2ecf20Sopenharmony_ci{ 2618c2ecf20Sopenharmony_ci struct gpio_fan_data *fan_data = dev_get_drvdata(dev); 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_ci return sprintf(buf, "%d\n", fan_data->speed[fan_data->speed_index].rpm); 2648c2ecf20Sopenharmony_ci} 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_cistatic ssize_t set_rpm(struct device *dev, struct device_attribute *attr, 2678c2ecf20Sopenharmony_ci const char *buf, size_t count) 2688c2ecf20Sopenharmony_ci{ 2698c2ecf20Sopenharmony_ci struct gpio_fan_data *fan_data = dev_get_drvdata(dev); 2708c2ecf20Sopenharmony_ci unsigned long rpm; 2718c2ecf20Sopenharmony_ci int ret = count; 2728c2ecf20Sopenharmony_ci 2738c2ecf20Sopenharmony_ci if (kstrtoul(buf, 10, &rpm)) 2748c2ecf20Sopenharmony_ci return -EINVAL; 2758c2ecf20Sopenharmony_ci 2768c2ecf20Sopenharmony_ci mutex_lock(&fan_data->lock); 2778c2ecf20Sopenharmony_ci 2788c2ecf20Sopenharmony_ci if (!fan_data->pwm_enable) { 2798c2ecf20Sopenharmony_ci ret = -EPERM; 2808c2ecf20Sopenharmony_ci goto exit_unlock; 2818c2ecf20Sopenharmony_ci } 2828c2ecf20Sopenharmony_ci 2838c2ecf20Sopenharmony_ci set_fan_speed(fan_data, rpm_to_speed_index(fan_data, rpm)); 2848c2ecf20Sopenharmony_ci 2858c2ecf20Sopenharmony_ciexit_unlock: 2868c2ecf20Sopenharmony_ci mutex_unlock(&fan_data->lock); 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_ci return ret; 2898c2ecf20Sopenharmony_ci} 2908c2ecf20Sopenharmony_ci 2918c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RW(pwm1); 2928c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RW(pwm1_enable); 2938c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(pwm1_mode); 2948c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(fan1_min); 2958c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(fan1_max); 2968c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(fan1_input); 2978c2ecf20Sopenharmony_cistatic DEVICE_ATTR(fan1_target, 0644, fan1_input_show, set_rpm); 2988c2ecf20Sopenharmony_ci 2998c2ecf20Sopenharmony_cistatic umode_t gpio_fan_is_visible(struct kobject *kobj, 3008c2ecf20Sopenharmony_ci struct attribute *attr, int index) 3018c2ecf20Sopenharmony_ci{ 3028c2ecf20Sopenharmony_ci struct device *dev = container_of(kobj, struct device, kobj); 3038c2ecf20Sopenharmony_ci struct gpio_fan_data *data = dev_get_drvdata(dev); 3048c2ecf20Sopenharmony_ci 3058c2ecf20Sopenharmony_ci if (index == 0 && !data->alarm_gpio) 3068c2ecf20Sopenharmony_ci return 0; 3078c2ecf20Sopenharmony_ci if (index > 0 && !data->gpios) 3088c2ecf20Sopenharmony_ci return 0; 3098c2ecf20Sopenharmony_ci 3108c2ecf20Sopenharmony_ci return attr->mode; 3118c2ecf20Sopenharmony_ci} 3128c2ecf20Sopenharmony_ci 3138c2ecf20Sopenharmony_cistatic struct attribute *gpio_fan_attributes[] = { 3148c2ecf20Sopenharmony_ci &dev_attr_fan1_alarm.attr, /* 0 */ 3158c2ecf20Sopenharmony_ci &dev_attr_pwm1.attr, /* 1 */ 3168c2ecf20Sopenharmony_ci &dev_attr_pwm1_enable.attr, 3178c2ecf20Sopenharmony_ci &dev_attr_pwm1_mode.attr, 3188c2ecf20Sopenharmony_ci &dev_attr_fan1_input.attr, 3198c2ecf20Sopenharmony_ci &dev_attr_fan1_target.attr, 3208c2ecf20Sopenharmony_ci &dev_attr_fan1_min.attr, 3218c2ecf20Sopenharmony_ci &dev_attr_fan1_max.attr, 3228c2ecf20Sopenharmony_ci NULL 3238c2ecf20Sopenharmony_ci}; 3248c2ecf20Sopenharmony_ci 3258c2ecf20Sopenharmony_cistatic const struct attribute_group gpio_fan_group = { 3268c2ecf20Sopenharmony_ci .attrs = gpio_fan_attributes, 3278c2ecf20Sopenharmony_ci .is_visible = gpio_fan_is_visible, 3288c2ecf20Sopenharmony_ci}; 3298c2ecf20Sopenharmony_ci 3308c2ecf20Sopenharmony_cistatic const struct attribute_group *gpio_fan_groups[] = { 3318c2ecf20Sopenharmony_ci &gpio_fan_group, 3328c2ecf20Sopenharmony_ci NULL 3338c2ecf20Sopenharmony_ci}; 3348c2ecf20Sopenharmony_ci 3358c2ecf20Sopenharmony_cistatic int fan_ctrl_init(struct gpio_fan_data *fan_data) 3368c2ecf20Sopenharmony_ci{ 3378c2ecf20Sopenharmony_ci int num_gpios = fan_data->num_gpios; 3388c2ecf20Sopenharmony_ci struct gpio_desc **gpios = fan_data->gpios; 3398c2ecf20Sopenharmony_ci int i, err; 3408c2ecf20Sopenharmony_ci 3418c2ecf20Sopenharmony_ci for (i = 0; i < num_gpios; i++) { 3428c2ecf20Sopenharmony_ci /* 3438c2ecf20Sopenharmony_ci * The GPIO descriptors were retrieved with GPIOD_ASIS so here 3448c2ecf20Sopenharmony_ci * we set the GPIO into output mode, carefully preserving the 3458c2ecf20Sopenharmony_ci * current value by setting it to whatever it is already set 3468c2ecf20Sopenharmony_ci * (no surprise changes in default fan speed). 3478c2ecf20Sopenharmony_ci */ 3488c2ecf20Sopenharmony_ci err = gpiod_direction_output(gpios[i], 3498c2ecf20Sopenharmony_ci gpiod_get_value_cansleep(gpios[i])); 3508c2ecf20Sopenharmony_ci if (err) 3518c2ecf20Sopenharmony_ci return err; 3528c2ecf20Sopenharmony_ci } 3538c2ecf20Sopenharmony_ci 3548c2ecf20Sopenharmony_ci fan_data->pwm_enable = true; /* Enable manual fan speed control. */ 3558c2ecf20Sopenharmony_ci fan_data->speed_index = get_fan_speed_index(fan_data); 3568c2ecf20Sopenharmony_ci if (fan_data->speed_index < 0) 3578c2ecf20Sopenharmony_ci return fan_data->speed_index; 3588c2ecf20Sopenharmony_ci 3598c2ecf20Sopenharmony_ci return 0; 3608c2ecf20Sopenharmony_ci} 3618c2ecf20Sopenharmony_ci 3628c2ecf20Sopenharmony_cistatic int gpio_fan_get_max_state(struct thermal_cooling_device *cdev, 3638c2ecf20Sopenharmony_ci unsigned long *state) 3648c2ecf20Sopenharmony_ci{ 3658c2ecf20Sopenharmony_ci struct gpio_fan_data *fan_data = cdev->devdata; 3668c2ecf20Sopenharmony_ci 3678c2ecf20Sopenharmony_ci if (!fan_data) 3688c2ecf20Sopenharmony_ci return -EINVAL; 3698c2ecf20Sopenharmony_ci 3708c2ecf20Sopenharmony_ci *state = fan_data->num_speed - 1; 3718c2ecf20Sopenharmony_ci return 0; 3728c2ecf20Sopenharmony_ci} 3738c2ecf20Sopenharmony_ci 3748c2ecf20Sopenharmony_cistatic int gpio_fan_get_cur_state(struct thermal_cooling_device *cdev, 3758c2ecf20Sopenharmony_ci unsigned long *state) 3768c2ecf20Sopenharmony_ci{ 3778c2ecf20Sopenharmony_ci struct gpio_fan_data *fan_data = cdev->devdata; 3788c2ecf20Sopenharmony_ci 3798c2ecf20Sopenharmony_ci if (!fan_data) 3808c2ecf20Sopenharmony_ci return -EINVAL; 3818c2ecf20Sopenharmony_ci 3828c2ecf20Sopenharmony_ci *state = fan_data->speed_index; 3838c2ecf20Sopenharmony_ci return 0; 3848c2ecf20Sopenharmony_ci} 3858c2ecf20Sopenharmony_ci 3868c2ecf20Sopenharmony_cistatic int gpio_fan_set_cur_state(struct thermal_cooling_device *cdev, 3878c2ecf20Sopenharmony_ci unsigned long state) 3888c2ecf20Sopenharmony_ci{ 3898c2ecf20Sopenharmony_ci struct gpio_fan_data *fan_data = cdev->devdata; 3908c2ecf20Sopenharmony_ci 3918c2ecf20Sopenharmony_ci if (!fan_data) 3928c2ecf20Sopenharmony_ci return -EINVAL; 3938c2ecf20Sopenharmony_ci 3948c2ecf20Sopenharmony_ci if (state >= fan_data->num_speed) 3958c2ecf20Sopenharmony_ci return -EINVAL; 3968c2ecf20Sopenharmony_ci 3978c2ecf20Sopenharmony_ci set_fan_speed(fan_data, state); 3988c2ecf20Sopenharmony_ci return 0; 3998c2ecf20Sopenharmony_ci} 4008c2ecf20Sopenharmony_ci 4018c2ecf20Sopenharmony_cistatic const struct thermal_cooling_device_ops gpio_fan_cool_ops = { 4028c2ecf20Sopenharmony_ci .get_max_state = gpio_fan_get_max_state, 4038c2ecf20Sopenharmony_ci .get_cur_state = gpio_fan_get_cur_state, 4048c2ecf20Sopenharmony_ci .set_cur_state = gpio_fan_set_cur_state, 4058c2ecf20Sopenharmony_ci}; 4068c2ecf20Sopenharmony_ci 4078c2ecf20Sopenharmony_ci/* 4088c2ecf20Sopenharmony_ci * Translate OpenFirmware node properties into platform_data 4098c2ecf20Sopenharmony_ci */ 4108c2ecf20Sopenharmony_cistatic int gpio_fan_get_of_data(struct gpio_fan_data *fan_data) 4118c2ecf20Sopenharmony_ci{ 4128c2ecf20Sopenharmony_ci struct gpio_fan_speed *speed; 4138c2ecf20Sopenharmony_ci struct device *dev = fan_data->dev; 4148c2ecf20Sopenharmony_ci struct device_node *np = dev->of_node; 4158c2ecf20Sopenharmony_ci struct gpio_desc **gpios; 4168c2ecf20Sopenharmony_ci unsigned i; 4178c2ecf20Sopenharmony_ci u32 u; 4188c2ecf20Sopenharmony_ci struct property *prop; 4198c2ecf20Sopenharmony_ci const __be32 *p; 4208c2ecf20Sopenharmony_ci 4218c2ecf20Sopenharmony_ci /* Alarm GPIO if one exists */ 4228c2ecf20Sopenharmony_ci fan_data->alarm_gpio = devm_gpiod_get_optional(dev, "alarm", GPIOD_IN); 4238c2ecf20Sopenharmony_ci if (IS_ERR(fan_data->alarm_gpio)) 4248c2ecf20Sopenharmony_ci return PTR_ERR(fan_data->alarm_gpio); 4258c2ecf20Sopenharmony_ci 4268c2ecf20Sopenharmony_ci /* Fill GPIO pin array */ 4278c2ecf20Sopenharmony_ci fan_data->num_gpios = gpiod_count(dev, NULL); 4288c2ecf20Sopenharmony_ci if (fan_data->num_gpios <= 0) { 4298c2ecf20Sopenharmony_ci if (fan_data->alarm_gpio) 4308c2ecf20Sopenharmony_ci return 0; 4318c2ecf20Sopenharmony_ci dev_err(dev, "DT properties empty / missing"); 4328c2ecf20Sopenharmony_ci return -ENODEV; 4338c2ecf20Sopenharmony_ci } 4348c2ecf20Sopenharmony_ci gpios = devm_kcalloc(dev, 4358c2ecf20Sopenharmony_ci fan_data->num_gpios, sizeof(struct gpio_desc *), 4368c2ecf20Sopenharmony_ci GFP_KERNEL); 4378c2ecf20Sopenharmony_ci if (!gpios) 4388c2ecf20Sopenharmony_ci return -ENOMEM; 4398c2ecf20Sopenharmony_ci for (i = 0; i < fan_data->num_gpios; i++) { 4408c2ecf20Sopenharmony_ci gpios[i] = devm_gpiod_get_index(dev, NULL, i, GPIOD_ASIS); 4418c2ecf20Sopenharmony_ci if (IS_ERR(gpios[i])) 4428c2ecf20Sopenharmony_ci return PTR_ERR(gpios[i]); 4438c2ecf20Sopenharmony_ci } 4448c2ecf20Sopenharmony_ci fan_data->gpios = gpios; 4458c2ecf20Sopenharmony_ci 4468c2ecf20Sopenharmony_ci /* Get number of RPM/ctrl_val pairs in speed map */ 4478c2ecf20Sopenharmony_ci prop = of_find_property(np, "gpio-fan,speed-map", &i); 4488c2ecf20Sopenharmony_ci if (!prop) { 4498c2ecf20Sopenharmony_ci dev_err(dev, "gpio-fan,speed-map DT property missing"); 4508c2ecf20Sopenharmony_ci return -ENODEV; 4518c2ecf20Sopenharmony_ci } 4528c2ecf20Sopenharmony_ci i = i / sizeof(u32); 4538c2ecf20Sopenharmony_ci if (i == 0 || i & 1) { 4548c2ecf20Sopenharmony_ci dev_err(dev, "gpio-fan,speed-map contains zero/odd number of entries"); 4558c2ecf20Sopenharmony_ci return -ENODEV; 4568c2ecf20Sopenharmony_ci } 4578c2ecf20Sopenharmony_ci fan_data->num_speed = i / 2; 4588c2ecf20Sopenharmony_ci 4598c2ecf20Sopenharmony_ci /* 4608c2ecf20Sopenharmony_ci * Populate speed map 4618c2ecf20Sopenharmony_ci * Speed map is in the form <RPM ctrl_val RPM ctrl_val ...> 4628c2ecf20Sopenharmony_ci * this needs splitting into pairs to create gpio_fan_speed structs 4638c2ecf20Sopenharmony_ci */ 4648c2ecf20Sopenharmony_ci speed = devm_kcalloc(dev, 4658c2ecf20Sopenharmony_ci fan_data->num_speed, sizeof(struct gpio_fan_speed), 4668c2ecf20Sopenharmony_ci GFP_KERNEL); 4678c2ecf20Sopenharmony_ci if (!speed) 4688c2ecf20Sopenharmony_ci return -ENOMEM; 4698c2ecf20Sopenharmony_ci p = NULL; 4708c2ecf20Sopenharmony_ci for (i = 0; i < fan_data->num_speed; i++) { 4718c2ecf20Sopenharmony_ci p = of_prop_next_u32(prop, p, &u); 4728c2ecf20Sopenharmony_ci if (!p) 4738c2ecf20Sopenharmony_ci return -ENODEV; 4748c2ecf20Sopenharmony_ci speed[i].rpm = u; 4758c2ecf20Sopenharmony_ci p = of_prop_next_u32(prop, p, &u); 4768c2ecf20Sopenharmony_ci if (!p) 4778c2ecf20Sopenharmony_ci return -ENODEV; 4788c2ecf20Sopenharmony_ci speed[i].ctrl_val = u; 4798c2ecf20Sopenharmony_ci } 4808c2ecf20Sopenharmony_ci fan_data->speed = speed; 4818c2ecf20Sopenharmony_ci 4828c2ecf20Sopenharmony_ci return 0; 4838c2ecf20Sopenharmony_ci} 4848c2ecf20Sopenharmony_ci 4858c2ecf20Sopenharmony_cistatic const struct of_device_id of_gpio_fan_match[] = { 4868c2ecf20Sopenharmony_ci { .compatible = "gpio-fan", }, 4878c2ecf20Sopenharmony_ci {}, 4888c2ecf20Sopenharmony_ci}; 4898c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, of_gpio_fan_match); 4908c2ecf20Sopenharmony_ci 4918c2ecf20Sopenharmony_cistatic void gpio_fan_stop(void *data) 4928c2ecf20Sopenharmony_ci{ 4938c2ecf20Sopenharmony_ci set_fan_speed(data, 0); 4948c2ecf20Sopenharmony_ci} 4958c2ecf20Sopenharmony_ci 4968c2ecf20Sopenharmony_cistatic int gpio_fan_probe(struct platform_device *pdev) 4978c2ecf20Sopenharmony_ci{ 4988c2ecf20Sopenharmony_ci int err; 4998c2ecf20Sopenharmony_ci struct gpio_fan_data *fan_data; 5008c2ecf20Sopenharmony_ci struct device *dev = &pdev->dev; 5018c2ecf20Sopenharmony_ci struct device_node *np = dev->of_node; 5028c2ecf20Sopenharmony_ci 5038c2ecf20Sopenharmony_ci fan_data = devm_kzalloc(dev, sizeof(struct gpio_fan_data), 5048c2ecf20Sopenharmony_ci GFP_KERNEL); 5058c2ecf20Sopenharmony_ci if (!fan_data) 5068c2ecf20Sopenharmony_ci return -ENOMEM; 5078c2ecf20Sopenharmony_ci 5088c2ecf20Sopenharmony_ci fan_data->dev = dev; 5098c2ecf20Sopenharmony_ci err = gpio_fan_get_of_data(fan_data); 5108c2ecf20Sopenharmony_ci if (err) 5118c2ecf20Sopenharmony_ci return err; 5128c2ecf20Sopenharmony_ci 5138c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, fan_data); 5148c2ecf20Sopenharmony_ci mutex_init(&fan_data->lock); 5158c2ecf20Sopenharmony_ci 5168c2ecf20Sopenharmony_ci /* Configure control GPIOs if available. */ 5178c2ecf20Sopenharmony_ci if (fan_data->gpios && fan_data->num_gpios > 0) { 5188c2ecf20Sopenharmony_ci if (!fan_data->speed || fan_data->num_speed <= 1) 5198c2ecf20Sopenharmony_ci return -EINVAL; 5208c2ecf20Sopenharmony_ci err = fan_ctrl_init(fan_data); 5218c2ecf20Sopenharmony_ci if (err) 5228c2ecf20Sopenharmony_ci return err; 5238c2ecf20Sopenharmony_ci err = devm_add_action_or_reset(dev, gpio_fan_stop, fan_data); 5248c2ecf20Sopenharmony_ci if (err) 5258c2ecf20Sopenharmony_ci return err; 5268c2ecf20Sopenharmony_ci } 5278c2ecf20Sopenharmony_ci 5288c2ecf20Sopenharmony_ci /* Make this driver part of hwmon class. */ 5298c2ecf20Sopenharmony_ci fan_data->hwmon_dev = 5308c2ecf20Sopenharmony_ci devm_hwmon_device_register_with_groups(dev, 5318c2ecf20Sopenharmony_ci "gpio_fan", fan_data, 5328c2ecf20Sopenharmony_ci gpio_fan_groups); 5338c2ecf20Sopenharmony_ci if (IS_ERR(fan_data->hwmon_dev)) 5348c2ecf20Sopenharmony_ci return PTR_ERR(fan_data->hwmon_dev); 5358c2ecf20Sopenharmony_ci 5368c2ecf20Sopenharmony_ci /* Configure alarm GPIO if available. */ 5378c2ecf20Sopenharmony_ci if (fan_data->alarm_gpio) { 5388c2ecf20Sopenharmony_ci err = fan_alarm_init(fan_data); 5398c2ecf20Sopenharmony_ci if (err) 5408c2ecf20Sopenharmony_ci return err; 5418c2ecf20Sopenharmony_ci } 5428c2ecf20Sopenharmony_ci 5438c2ecf20Sopenharmony_ci /* Optional cooling device register for Device tree platforms */ 5448c2ecf20Sopenharmony_ci fan_data->cdev = devm_thermal_of_cooling_device_register(dev, np, 5458c2ecf20Sopenharmony_ci "gpio-fan", fan_data, &gpio_fan_cool_ops); 5468c2ecf20Sopenharmony_ci 5478c2ecf20Sopenharmony_ci dev_info(dev, "GPIO fan initialized\n"); 5488c2ecf20Sopenharmony_ci 5498c2ecf20Sopenharmony_ci return 0; 5508c2ecf20Sopenharmony_ci} 5518c2ecf20Sopenharmony_ci 5528c2ecf20Sopenharmony_cistatic void gpio_fan_shutdown(struct platform_device *pdev) 5538c2ecf20Sopenharmony_ci{ 5548c2ecf20Sopenharmony_ci struct gpio_fan_data *fan_data = platform_get_drvdata(pdev); 5558c2ecf20Sopenharmony_ci 5568c2ecf20Sopenharmony_ci if (fan_data->gpios) 5578c2ecf20Sopenharmony_ci set_fan_speed(fan_data, 0); 5588c2ecf20Sopenharmony_ci} 5598c2ecf20Sopenharmony_ci 5608c2ecf20Sopenharmony_ci#ifdef CONFIG_PM_SLEEP 5618c2ecf20Sopenharmony_cistatic int gpio_fan_suspend(struct device *dev) 5628c2ecf20Sopenharmony_ci{ 5638c2ecf20Sopenharmony_ci struct gpio_fan_data *fan_data = dev_get_drvdata(dev); 5648c2ecf20Sopenharmony_ci 5658c2ecf20Sopenharmony_ci if (fan_data->gpios) { 5668c2ecf20Sopenharmony_ci fan_data->resume_speed = fan_data->speed_index; 5678c2ecf20Sopenharmony_ci set_fan_speed(fan_data, 0); 5688c2ecf20Sopenharmony_ci } 5698c2ecf20Sopenharmony_ci 5708c2ecf20Sopenharmony_ci return 0; 5718c2ecf20Sopenharmony_ci} 5728c2ecf20Sopenharmony_ci 5738c2ecf20Sopenharmony_cistatic int gpio_fan_resume(struct device *dev) 5748c2ecf20Sopenharmony_ci{ 5758c2ecf20Sopenharmony_ci struct gpio_fan_data *fan_data = dev_get_drvdata(dev); 5768c2ecf20Sopenharmony_ci 5778c2ecf20Sopenharmony_ci if (fan_data->gpios) 5788c2ecf20Sopenharmony_ci set_fan_speed(fan_data, fan_data->resume_speed); 5798c2ecf20Sopenharmony_ci 5808c2ecf20Sopenharmony_ci return 0; 5818c2ecf20Sopenharmony_ci} 5828c2ecf20Sopenharmony_ci 5838c2ecf20Sopenharmony_cistatic SIMPLE_DEV_PM_OPS(gpio_fan_pm, gpio_fan_suspend, gpio_fan_resume); 5848c2ecf20Sopenharmony_ci#define GPIO_FAN_PM (&gpio_fan_pm) 5858c2ecf20Sopenharmony_ci#else 5868c2ecf20Sopenharmony_ci#define GPIO_FAN_PM NULL 5878c2ecf20Sopenharmony_ci#endif 5888c2ecf20Sopenharmony_ci 5898c2ecf20Sopenharmony_cistatic struct platform_driver gpio_fan_driver = { 5908c2ecf20Sopenharmony_ci .probe = gpio_fan_probe, 5918c2ecf20Sopenharmony_ci .shutdown = gpio_fan_shutdown, 5928c2ecf20Sopenharmony_ci .driver = { 5938c2ecf20Sopenharmony_ci .name = "gpio-fan", 5948c2ecf20Sopenharmony_ci .pm = GPIO_FAN_PM, 5958c2ecf20Sopenharmony_ci .of_match_table = of_match_ptr(of_gpio_fan_match), 5968c2ecf20Sopenharmony_ci }, 5978c2ecf20Sopenharmony_ci}; 5988c2ecf20Sopenharmony_ci 5998c2ecf20Sopenharmony_cimodule_platform_driver(gpio_fan_driver); 6008c2ecf20Sopenharmony_ci 6018c2ecf20Sopenharmony_ciMODULE_AUTHOR("Simon Guinot <sguinot@lacie.com>"); 6028c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("GPIO FAN driver"); 6038c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 6048c2ecf20Sopenharmony_ciMODULE_ALIAS("platform:gpio-fan"); 605