blob: 000c8d2e0987166dc0ee5ce3cb73bc3ff7ddfcc7 [file] [log] [blame]
Simon Guinotd6fe1362010-10-22 00:44:19 +02001/*
2 * gpio-fan.c - Hwmon driver for fans connected to GPIO lines.
3 *
4 * Copyright (C) 2010 LaCie
5 *
6 * Author: Simon Guinot <sguinot@lacie.com>
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21 */
22
23#include <linux/module.h>
24#include <linux/init.h>
25#include <linux/slab.h>
26#include <linux/interrupt.h>
27#include <linux/irq.h>
28#include <linux/platform_device.h>
29#include <linux/err.h>
30#include <linux/mutex.h>
31#include <linux/hwmon.h>
32#include <linux/gpio.h>
Sachin Kamatc50588a2013-09-27 16:56:00 +053033#include <linux/of.h>
Jamie Lentin55fb8b062012-09-14 17:07:06 +010034#include <linux/of_platform.h>
35#include <linux/of_gpio.h>
Nishanth Menonb5cf88e2015-01-08 12:05:03 -060036#include <linux/thermal.h>
Simon Guinotd6fe1362010-10-22 00:44:19 +020037
Linus Walleijef7a6122017-09-26 01:09:05 +020038struct gpio_fan_alarm {
39 unsigned int gpio;
40 unsigned int active_low;
41};
42
43struct gpio_fan_speed {
44 int rpm;
45 int ctrl_val;
46};
47
Simon Guinotd6fe1362010-10-22 00:44:19 +020048struct gpio_fan_data {
Linus Walleij8c0eb9b2017-09-26 01:09:06 +020049 struct device *dev;
Simon Guinotd6fe1362010-10-22 00:44:19 +020050 struct device *hwmon_dev;
Nishanth Menonb5cf88e2015-01-08 12:05:03 -060051 /* Cooling device if any */
52 struct thermal_cooling_device *cdev;
Simon Guinotd6fe1362010-10-22 00:44:19 +020053 struct mutex lock; /* lock GPIOs operations. */
54 int num_ctrl;
55 unsigned *ctrl;
56 int num_speed;
57 struct gpio_fan_speed *speed;
58 int speed_index;
Rafael J. Wysocki6d20a6c2012-07-08 00:01:03 +020059#ifdef CONFIG_PM_SLEEP
Simon Guinotd6fe1362010-10-22 00:44:19 +020060 int resume_speed;
61#endif
62 bool pwm_enable;
63 struct gpio_fan_alarm *alarm;
64 struct work_struct alarm_work;
65};
66
67/*
68 * Alarm GPIO.
69 */
70
71static void fan_alarm_notify(struct work_struct *ws)
72{
73 struct gpio_fan_data *fan_data =
74 container_of(ws, struct gpio_fan_data, alarm_work);
75
Linus Walleij8c0eb9b2017-09-26 01:09:06 +020076 sysfs_notify(&fan_data->dev->kobj, NULL, "fan1_alarm");
77 kobject_uevent(&fan_data->dev->kobj, KOBJ_CHANGE);
Simon Guinotd6fe1362010-10-22 00:44:19 +020078}
79
80static irqreturn_t fan_alarm_irq_handler(int irq, void *dev_id)
81{
82 struct gpio_fan_data *fan_data = dev_id;
83
84 schedule_work(&fan_data->alarm_work);
85
86 return IRQ_NONE;
87}
88
Julia Lawallc490c632016-12-22 13:04:44 +010089static ssize_t fan1_alarm_show(struct device *dev,
90 struct device_attribute *attr, char *buf)
Simon Guinotd6fe1362010-10-22 00:44:19 +020091{
92 struct gpio_fan_data *fan_data = dev_get_drvdata(dev);
93 struct gpio_fan_alarm *alarm = fan_data->alarm;
Nishanth Menon52a95c12014-12-04 10:58:47 -060094 int value = gpio_get_value_cansleep(alarm->gpio);
Simon Guinotd6fe1362010-10-22 00:44:19 +020095
96 if (alarm->active_low)
97 value = !value;
98
99 return sprintf(buf, "%d\n", value);
100}
101
Julia Lawallc490c632016-12-22 13:04:44 +0100102static DEVICE_ATTR_RO(fan1_alarm);
Simon Guinotd6fe1362010-10-22 00:44:19 +0200103
Linus Walleijb5482f72017-09-26 01:09:08 +0200104static int fan_alarm_init(struct gpio_fan_data *fan_data)
Simon Guinotd6fe1362010-10-22 00:44:19 +0200105{
106 int err;
107 int alarm_irq;
Linus Walleij8c0eb9b2017-09-26 01:09:06 +0200108 struct device *dev = fan_data->dev;
Linus Walleijb5482f72017-09-26 01:09:08 +0200109 struct gpio_fan_alarm *alarm = fan_data->alarm;
Simon Guinotd6fe1362010-10-22 00:44:19 +0200110
Linus Walleij8c0eb9b2017-09-26 01:09:06 +0200111 err = devm_gpio_request(dev, alarm->gpio, "GPIO fan alarm");
Simon Guinotd6fe1362010-10-22 00:44:19 +0200112 if (err)
113 return err;
114
115 err = gpio_direction_input(alarm->gpio);
116 if (err)
Guenter Roeckd00985f2012-06-02 09:58:07 -0700117 return err;
Simon Guinotd6fe1362010-10-22 00:44:19 +0200118
Simon Guinotd6fe1362010-10-22 00:44:19 +0200119 /*
120 * If the alarm GPIO don't support interrupts, just leave
121 * without initializing the fail notification support.
122 */
123 alarm_irq = gpio_to_irq(alarm->gpio);
124 if (alarm_irq < 0)
125 return 0;
126
127 INIT_WORK(&fan_data->alarm_work, fan_alarm_notify);
Thomas Gleixnerdced35a2011-03-28 17:49:12 +0200128 irq_set_irq_type(alarm_irq, IRQ_TYPE_EDGE_BOTH);
Linus Walleij8c0eb9b2017-09-26 01:09:06 +0200129 err = devm_request_irq(dev, alarm_irq, fan_alarm_irq_handler,
Guenter Roeckd00985f2012-06-02 09:58:07 -0700130 IRQF_SHARED, "GPIO fan alarm", fan_data);
Simon Guinotd6fe1362010-10-22 00:44:19 +0200131 return err;
132}
133
Simon Guinotd6fe1362010-10-22 00:44:19 +0200134/*
135 * Control GPIOs.
136 */
137
138/* Must be called with fan_data->lock held, except during initialization. */
139static void __set_fan_ctrl(struct gpio_fan_data *fan_data, int ctrl_val)
140{
141 int i;
142
143 for (i = 0; i < fan_data->num_ctrl; i++)
Nishanth Menon52a95c12014-12-04 10:58:47 -0600144 gpio_set_value_cansleep(fan_data->ctrl[i], (ctrl_val >> i) & 1);
Simon Guinotd6fe1362010-10-22 00:44:19 +0200145}
146
147static int __get_fan_ctrl(struct gpio_fan_data *fan_data)
148{
149 int i;
150 int ctrl_val = 0;
151
152 for (i = 0; i < fan_data->num_ctrl; i++) {
153 int value;
154
Nishanth Menon52a95c12014-12-04 10:58:47 -0600155 value = gpio_get_value_cansleep(fan_data->ctrl[i]);
Simon Guinotd6fe1362010-10-22 00:44:19 +0200156 ctrl_val |= (value << i);
157 }
158 return ctrl_val;
159}
160
161/* Must be called with fan_data->lock held, except during initialization. */
162static void set_fan_speed(struct gpio_fan_data *fan_data, int speed_index)
163{
164 if (fan_data->speed_index == speed_index)
165 return;
166
167 __set_fan_ctrl(fan_data, fan_data->speed[speed_index].ctrl_val);
168 fan_data->speed_index = speed_index;
169}
170
171static int get_fan_speed_index(struct gpio_fan_data *fan_data)
172{
173 int ctrl_val = __get_fan_ctrl(fan_data);
174 int i;
175
176 for (i = 0; i < fan_data->num_speed; i++)
177 if (fan_data->speed[i].ctrl_val == ctrl_val)
178 return i;
179
Linus Walleij8c0eb9b2017-09-26 01:09:06 +0200180 dev_warn(fan_data->dev,
Simon Guinotd6fe1362010-10-22 00:44:19 +0200181 "missing speed array entry for GPIO value 0x%x\n", ctrl_val);
182
Guenter Roeckc52ae3d2013-09-13 10:42:39 -0700183 return -ENODEV;
Simon Guinotd6fe1362010-10-22 00:44:19 +0200184}
185
Axel Lin2565fb02014-08-02 13:36:38 +0800186static int rpm_to_speed_index(struct gpio_fan_data *fan_data, unsigned long rpm)
Simon Guinotd6fe1362010-10-22 00:44:19 +0200187{
188 struct gpio_fan_speed *speed = fan_data->speed;
189 int i;
190
191 for (i = 0; i < fan_data->num_speed; i++)
192 if (speed[i].rpm >= rpm)
193 return i;
194
195 return fan_data->num_speed - 1;
196}
197
Julia Lawallc490c632016-12-22 13:04:44 +0100198static ssize_t pwm1_show(struct device *dev, struct device_attribute *attr,
199 char *buf)
Simon Guinotd6fe1362010-10-22 00:44:19 +0200200{
201 struct gpio_fan_data *fan_data = dev_get_drvdata(dev);
202 u8 pwm = fan_data->speed_index * 255 / (fan_data->num_speed - 1);
203
204 return sprintf(buf, "%d\n", pwm);
205}
206
Julia Lawallc490c632016-12-22 13:04:44 +0100207static ssize_t pwm1_store(struct device *dev, struct device_attribute *attr,
208 const char *buf, size_t count)
Simon Guinotd6fe1362010-10-22 00:44:19 +0200209{
210 struct gpio_fan_data *fan_data = dev_get_drvdata(dev);
211 unsigned long pwm;
212 int speed_index;
213 int ret = count;
214
Frans Meulenbroeks179c4fd2012-01-04 20:58:52 +0100215 if (kstrtoul(buf, 10, &pwm) || pwm > 255)
Simon Guinotd6fe1362010-10-22 00:44:19 +0200216 return -EINVAL;
217
218 mutex_lock(&fan_data->lock);
219
220 if (!fan_data->pwm_enable) {
221 ret = -EPERM;
222 goto exit_unlock;
223 }
224
225 speed_index = DIV_ROUND_UP(pwm * (fan_data->num_speed - 1), 255);
226 set_fan_speed(fan_data, speed_index);
227
228exit_unlock:
229 mutex_unlock(&fan_data->lock);
230
231 return ret;
232}
233
Julia Lawallc490c632016-12-22 13:04:44 +0100234static ssize_t pwm1_enable_show(struct device *dev,
235 struct device_attribute *attr, char *buf)
Simon Guinotd6fe1362010-10-22 00:44:19 +0200236{
237 struct gpio_fan_data *fan_data = dev_get_drvdata(dev);
238
239 return sprintf(buf, "%d\n", fan_data->pwm_enable);
240}
241
Julia Lawallc490c632016-12-22 13:04:44 +0100242static ssize_t pwm1_enable_store(struct device *dev,
243 struct device_attribute *attr,
244 const char *buf, size_t count)
Simon Guinotd6fe1362010-10-22 00:44:19 +0200245{
246 struct gpio_fan_data *fan_data = dev_get_drvdata(dev);
247 unsigned long val;
248
Frans Meulenbroeks179c4fd2012-01-04 20:58:52 +0100249 if (kstrtoul(buf, 10, &val) || val > 1)
Simon Guinotd6fe1362010-10-22 00:44:19 +0200250 return -EINVAL;
251
252 if (fan_data->pwm_enable == val)
253 return count;
254
255 mutex_lock(&fan_data->lock);
256
257 fan_data->pwm_enable = val;
258
259 /* Disable manual control mode: set fan at full speed. */
260 if (val == 0)
261 set_fan_speed(fan_data, fan_data->num_speed - 1);
262
263 mutex_unlock(&fan_data->lock);
264
265 return count;
266}
267
Julia Lawallc490c632016-12-22 13:04:44 +0100268static ssize_t pwm1_mode_show(struct device *dev,
269 struct device_attribute *attr, char *buf)
Simon Guinotd6fe1362010-10-22 00:44:19 +0200270{
271 return sprintf(buf, "0\n");
272}
273
Julia Lawallc490c632016-12-22 13:04:44 +0100274static ssize_t fan1_min_show(struct device *dev,
275 struct device_attribute *attr, char *buf)
Simon Guinotd6fe1362010-10-22 00:44:19 +0200276{
277 struct gpio_fan_data *fan_data = dev_get_drvdata(dev);
278
279 return sprintf(buf, "%d\n", fan_data->speed[0].rpm);
280}
281
Julia Lawallc490c632016-12-22 13:04:44 +0100282static ssize_t fan1_max_show(struct device *dev,
283 struct device_attribute *attr, char *buf)
Simon Guinotd6fe1362010-10-22 00:44:19 +0200284{
285 struct gpio_fan_data *fan_data = dev_get_drvdata(dev);
286
287 return sprintf(buf, "%d\n",
288 fan_data->speed[fan_data->num_speed - 1].rpm);
289}
290
Julia Lawallc490c632016-12-22 13:04:44 +0100291static ssize_t fan1_input_show(struct device *dev,
292 struct device_attribute *attr, char *buf)
Simon Guinotd6fe1362010-10-22 00:44:19 +0200293{
294 struct gpio_fan_data *fan_data = dev_get_drvdata(dev);
295
296 return sprintf(buf, "%d\n", fan_data->speed[fan_data->speed_index].rpm);
297}
298
299static ssize_t set_rpm(struct device *dev, struct device_attribute *attr,
300 const char *buf, size_t count)
301{
302 struct gpio_fan_data *fan_data = dev_get_drvdata(dev);
303 unsigned long rpm;
304 int ret = count;
305
Frans Meulenbroeks179c4fd2012-01-04 20:58:52 +0100306 if (kstrtoul(buf, 10, &rpm))
Simon Guinotd6fe1362010-10-22 00:44:19 +0200307 return -EINVAL;
308
309 mutex_lock(&fan_data->lock);
310
311 if (!fan_data->pwm_enable) {
312 ret = -EPERM;
313 goto exit_unlock;
314 }
315
316 set_fan_speed(fan_data, rpm_to_speed_index(fan_data, rpm));
317
318exit_unlock:
319 mutex_unlock(&fan_data->lock);
320
321 return ret;
322}
323
Julia Lawallc490c632016-12-22 13:04:44 +0100324static DEVICE_ATTR_RW(pwm1);
325static DEVICE_ATTR_RW(pwm1_enable);
326static DEVICE_ATTR_RO(pwm1_mode);
327static DEVICE_ATTR_RO(fan1_min);
328static DEVICE_ATTR_RO(fan1_max);
329static DEVICE_ATTR_RO(fan1_input);
330static DEVICE_ATTR(fan1_target, S_IRUGO | S_IWUSR, fan1_input_show, set_rpm);
Simon Guinotd6fe1362010-10-22 00:44:19 +0200331
Guenter Roeckc81cc5a2013-03-30 09:09:39 -0700332static umode_t gpio_fan_is_visible(struct kobject *kobj,
333 struct attribute *attr, int index)
334{
335 struct device *dev = container_of(kobj, struct device, kobj);
336 struct gpio_fan_data *data = dev_get_drvdata(dev);
337
Guenter Roeck7258a122013-07-06 09:46:14 -0700338 if (index == 0 && !data->alarm)
Guenter Roeckc81cc5a2013-03-30 09:09:39 -0700339 return 0;
Guenter Roeck7258a122013-07-06 09:46:14 -0700340 if (index > 0 && !data->ctrl)
Guenter Roeckc81cc5a2013-03-30 09:09:39 -0700341 return 0;
342
343 return attr->mode;
344}
345
346static struct attribute *gpio_fan_attributes[] = {
Guenter Roeck7258a122013-07-06 09:46:14 -0700347 &dev_attr_fan1_alarm.attr, /* 0 */
348 &dev_attr_pwm1.attr, /* 1 */
Simon Guinotd6fe1362010-10-22 00:44:19 +0200349 &dev_attr_pwm1_enable.attr,
350 &dev_attr_pwm1_mode.attr,
351 &dev_attr_fan1_input.attr,
352 &dev_attr_fan1_target.attr,
353 &dev_attr_fan1_min.attr,
354 &dev_attr_fan1_max.attr,
355 NULL
356};
357
Guenter Roeckc81cc5a2013-03-30 09:09:39 -0700358static const struct attribute_group gpio_fan_group = {
359 .attrs = gpio_fan_attributes,
360 .is_visible = gpio_fan_is_visible,
Simon Guinotd6fe1362010-10-22 00:44:19 +0200361};
362
Guenter Roeck7258a122013-07-06 09:46:14 -0700363static const struct attribute_group *gpio_fan_groups[] = {
364 &gpio_fan_group,
365 NULL
366};
367
Linus Walleijb5482f72017-09-26 01:09:08 +0200368static int fan_ctrl_init(struct gpio_fan_data *fan_data)
Simon Guinotd6fe1362010-10-22 00:44:19 +0200369{
Linus Walleij8c0eb9b2017-09-26 01:09:06 +0200370 struct device *dev = fan_data->dev;
Linus Walleijb5482f72017-09-26 01:09:08 +0200371 int num_ctrl = fan_data->num_ctrl;
372 unsigned int *ctrl = fan_data->ctrl;
Simon Guinotd6fe1362010-10-22 00:44:19 +0200373 int i, err;
374
375 for (i = 0; i < num_ctrl; i++) {
Linus Walleij8c0eb9b2017-09-26 01:09:06 +0200376 err = devm_gpio_request(dev, ctrl[i],
Guenter Roeckd00985f2012-06-02 09:58:07 -0700377 "GPIO fan control");
Simon Guinotd6fe1362010-10-22 00:44:19 +0200378 if (err)
Guenter Roeckd00985f2012-06-02 09:58:07 -0700379 return err;
Simon Guinotd6fe1362010-10-22 00:44:19 +0200380
Nishanth Menon52a95c12014-12-04 10:58:47 -0600381 err = gpio_direction_output(ctrl[i],
382 gpio_get_value_cansleep(ctrl[i]));
Guenter Roeckd00985f2012-06-02 09:58:07 -0700383 if (err)
384 return err;
Simon Guinotd6fe1362010-10-22 00:44:19 +0200385 }
386
Simon Guinotd6fe1362010-10-22 00:44:19 +0200387 fan_data->pwm_enable = true; /* Enable manual fan speed control. */
388 fan_data->speed_index = get_fan_speed_index(fan_data);
Guenter Roeckd00985f2012-06-02 09:58:07 -0700389 if (fan_data->speed_index < 0)
Guenter Roeckc52ae3d2013-09-13 10:42:39 -0700390 return fan_data->speed_index;
Simon Guinotd6fe1362010-10-22 00:44:19 +0200391
Guenter Roeckc81cc5a2013-03-30 09:09:39 -0700392 return 0;
Simon Guinotd6fe1362010-10-22 00:44:19 +0200393}
394
Nishanth Menonb5cf88e2015-01-08 12:05:03 -0600395static int gpio_fan_get_max_state(struct thermal_cooling_device *cdev,
396 unsigned long *state)
397{
398 struct gpio_fan_data *fan_data = cdev->devdata;
399
400 if (!fan_data)
401 return -EINVAL;
402
403 *state = fan_data->num_speed - 1;
404 return 0;
405}
406
407static int gpio_fan_get_cur_state(struct thermal_cooling_device *cdev,
408 unsigned long *state)
409{
410 struct gpio_fan_data *fan_data = cdev->devdata;
Nishanth Menonb5cf88e2015-01-08 12:05:03 -0600411
412 if (!fan_data)
413 return -EINVAL;
414
Nishanth Menon000e09492016-02-19 18:09:51 -0600415 *state = fan_data->speed_index;
Nishanth Menonb5cf88e2015-01-08 12:05:03 -0600416 return 0;
417}
418
419static int gpio_fan_set_cur_state(struct thermal_cooling_device *cdev,
420 unsigned long state)
421{
422 struct gpio_fan_data *fan_data = cdev->devdata;
423
424 if (!fan_data)
425 return -EINVAL;
426
427 set_fan_speed(fan_data, state);
428 return 0;
429}
430
431static const struct thermal_cooling_device_ops gpio_fan_cool_ops = {
432 .get_max_state = gpio_fan_get_max_state,
433 .get_cur_state = gpio_fan_get_cur_state,
434 .set_cur_state = gpio_fan_set_cur_state,
435};
436
Jamie Lentin55fb8b062012-09-14 17:07:06 +0100437/*
438 * Translate OpenFirmware node properties into platform_data
439 */
Linus Walleijb5482f72017-09-26 01:09:08 +0200440static int gpio_fan_get_of_data(struct gpio_fan_data *fan_data)
Jamie Lentin55fb8b062012-09-14 17:07:06 +0100441{
Jamie Lentin55fb8b062012-09-14 17:07:06 +0100442 struct gpio_fan_speed *speed;
Linus Walleijb5482f72017-09-26 01:09:08 +0200443 struct device *dev = fan_data->dev;
444 struct device_node *np = dev->of_node;
Jamie Lentin55fb8b062012-09-14 17:07:06 +0100445 unsigned *ctrl;
446 unsigned i;
447 u32 u;
448 struct property *prop;
449 const __be32 *p;
450
Simon Guinot73ef85f2015-02-25 18:58:19 +0100451 /* Alarm GPIO if one exists */
Linus Walleijb5482f72017-09-26 01:09:08 +0200452 if (of_gpio_named_count(np, "alarm-gpios") > 0) {
Simon Guinot73ef85f2015-02-25 18:58:19 +0100453 struct gpio_fan_alarm *alarm;
454 int val;
455 enum of_gpio_flags flags;
456
457 alarm = devm_kzalloc(dev, sizeof(struct gpio_fan_alarm),
458 GFP_KERNEL);
459 if (!alarm)
460 return -ENOMEM;
461
Linus Walleijb5482f72017-09-26 01:09:08 +0200462 val = of_get_named_gpio_flags(np, "alarm-gpios", 0, &flags);
Simon Guinot73ef85f2015-02-25 18:58:19 +0100463 if (val < 0)
464 return val;
465 alarm->gpio = val;
466 alarm->active_low = flags & OF_GPIO_ACTIVE_LOW;
467
Linus Walleijb5482f72017-09-26 01:09:08 +0200468 fan_data->alarm = alarm;
Simon Guinot73ef85f2015-02-25 18:58:19 +0100469 }
470
Jamie Lentin55fb8b062012-09-14 17:07:06 +0100471 /* Fill GPIO pin array */
Linus Walleijb5482f72017-09-26 01:09:08 +0200472 fan_data->num_ctrl = of_gpio_count(np);
473 if (fan_data->num_ctrl <= 0) {
474 if (fan_data->alarm)
Simon Guinot73ef85f2015-02-25 18:58:19 +0100475 return 0;
476 dev_err(dev, "DT properties empty / missing");
Jamie Lentin55fb8b062012-09-14 17:07:06 +0100477 return -ENODEV;
478 }
Linus Walleijb5482f72017-09-26 01:09:08 +0200479 ctrl = devm_kzalloc(dev, fan_data->num_ctrl * sizeof(unsigned int),
480 GFP_KERNEL);
Jamie Lentin55fb8b062012-09-14 17:07:06 +0100481 if (!ctrl)
482 return -ENOMEM;
Linus Walleijb5482f72017-09-26 01:09:08 +0200483 for (i = 0; i < fan_data->num_ctrl; i++) {
Jamie Lentin55fb8b062012-09-14 17:07:06 +0100484 int val;
485
Linus Walleijb5482f72017-09-26 01:09:08 +0200486 val = of_get_gpio(np, i);
Jamie Lentin55fb8b062012-09-14 17:07:06 +0100487 if (val < 0)
488 return val;
489 ctrl[i] = val;
490 }
Linus Walleijb5482f72017-09-26 01:09:08 +0200491 fan_data->ctrl = ctrl;
Jamie Lentin55fb8b062012-09-14 17:07:06 +0100492
493 /* Get number of RPM/ctrl_val pairs in speed map */
Linus Walleijb5482f72017-09-26 01:09:08 +0200494 prop = of_find_property(np, "gpio-fan,speed-map", &i);
Jamie Lentin55fb8b062012-09-14 17:07:06 +0100495 if (!prop) {
496 dev_err(dev, "gpio-fan,speed-map DT property missing");
497 return -ENODEV;
498 }
499 i = i / sizeof(u32);
500 if (i == 0 || i & 1) {
501 dev_err(dev, "gpio-fan,speed-map contains zero/odd number of entries");
502 return -ENODEV;
503 }
Linus Walleijb5482f72017-09-26 01:09:08 +0200504 fan_data->num_speed = i / 2;
Jamie Lentin55fb8b062012-09-14 17:07:06 +0100505
506 /*
507 * Populate speed map
508 * Speed map is in the form <RPM ctrl_val RPM ctrl_val ...>
509 * this needs splitting into pairs to create gpio_fan_speed structs
510 */
511 speed = devm_kzalloc(dev,
Linus Walleijb5482f72017-09-26 01:09:08 +0200512 fan_data->num_speed * sizeof(struct gpio_fan_speed),
Jamie Lentin55fb8b062012-09-14 17:07:06 +0100513 GFP_KERNEL);
514 if (!speed)
515 return -ENOMEM;
516 p = NULL;
Linus Walleijb5482f72017-09-26 01:09:08 +0200517 for (i = 0; i < fan_data->num_speed; i++) {
Jamie Lentin55fb8b062012-09-14 17:07:06 +0100518 p = of_prop_next_u32(prop, p, &u);
519 if (!p)
520 return -ENODEV;
521 speed[i].rpm = u;
522 p = of_prop_next_u32(prop, p, &u);
523 if (!p)
524 return -ENODEV;
525 speed[i].ctrl_val = u;
526 }
Linus Walleijb5482f72017-09-26 01:09:08 +0200527 fan_data->speed = speed;
Jamie Lentin55fb8b062012-09-14 17:07:06 +0100528
Jamie Lentin55fb8b062012-09-14 17:07:06 +0100529 return 0;
530}
531
Jingoo Han6de709c2014-05-07 17:27:54 +0900532static const struct of_device_id of_gpio_fan_match[] = {
Jamie Lentin55fb8b062012-09-14 17:07:06 +0100533 { .compatible = "gpio-fan", },
534 {},
535};
Luis de Bethencourtfe515282015-09-17 18:09:28 +0200536MODULE_DEVICE_TABLE(of, of_gpio_fan_match);
Jamie Lentin55fb8b062012-09-14 17:07:06 +0100537
Bill Pemberton6c931ae2012-11-19 13:22:35 -0500538static int gpio_fan_probe(struct platform_device *pdev)
Simon Guinotd6fe1362010-10-22 00:44:19 +0200539{
540 int err;
541 struct gpio_fan_data *fan_data;
Linus Walleijf9013c12017-09-26 01:09:04 +0200542 struct device *dev = &pdev->dev;
543 struct device_node *np = dev->of_node;
Simon Guinotd6fe1362010-10-22 00:44:19 +0200544
Linus Walleijf9013c12017-09-26 01:09:04 +0200545 fan_data = devm_kzalloc(dev, sizeof(struct gpio_fan_data),
Nishanth Menonb5cf88e2015-01-08 12:05:03 -0600546 GFP_KERNEL);
547 if (!fan_data)
548 return -ENOMEM;
549
Linus Walleijb5482f72017-09-26 01:09:08 +0200550 err = gpio_fan_get_of_data(fan_data);
Linus Walleija9b4c8a2017-09-26 01:09:07 +0200551 if (err)
552 return err;
Simon Guinotd6fe1362010-10-22 00:44:19 +0200553
Linus Walleij8c0eb9b2017-09-26 01:09:06 +0200554 fan_data->dev = dev;
Simon Guinotd6fe1362010-10-22 00:44:19 +0200555 platform_set_drvdata(pdev, fan_data);
556 mutex_init(&fan_data->lock);
557
558 /* Configure alarm GPIO if available. */
Linus Walleijb5482f72017-09-26 01:09:08 +0200559 if (fan_data->alarm) {
560 err = fan_alarm_init(fan_data);
Simon Guinotd6fe1362010-10-22 00:44:19 +0200561 if (err)
Guenter Roeckd00985f2012-06-02 09:58:07 -0700562 return err;
Simon Guinotd6fe1362010-10-22 00:44:19 +0200563 }
564
565 /* Configure control GPIOs if available. */
Linus Walleijb5482f72017-09-26 01:09:08 +0200566 if (fan_data->ctrl && fan_data->num_ctrl > 0) {
567 if (!fan_data->speed || fan_data->num_speed <= 1)
Guenter Roeckc81cc5a2013-03-30 09:09:39 -0700568 return -EINVAL;
Linus Walleijb5482f72017-09-26 01:09:08 +0200569 err = fan_ctrl_init(fan_data);
Simon Guinotd6fe1362010-10-22 00:44:19 +0200570 if (err)
Guenter Roeckc81cc5a2013-03-30 09:09:39 -0700571 return err;
Simon Guinotd6fe1362010-10-22 00:44:19 +0200572 }
573
Simon Guinotd6fe1362010-10-22 00:44:19 +0200574 /* Make this driver part of hwmon class. */
Axel Lin49153b02014-06-14 14:50:50 +0800575 fan_data->hwmon_dev =
Linus Walleijf9013c12017-09-26 01:09:04 +0200576 devm_hwmon_device_register_with_groups(dev,
Axel Lin49153b02014-06-14 14:50:50 +0800577 "gpio_fan", fan_data,
578 gpio_fan_groups);
Guenter Roeck7258a122013-07-06 09:46:14 -0700579 if (IS_ERR(fan_data->hwmon_dev))
580 return PTR_ERR(fan_data->hwmon_dev);
Linus Walleija9b4c8a2017-09-26 01:09:07 +0200581
Nishanth Menone76ea262015-04-08 18:23:52 -0500582 /* Optional cooling device register for Device tree platforms */
Linus Walleijf9013c12017-09-26 01:09:04 +0200583 fan_data->cdev = thermal_of_cooling_device_register(np,
Nishanth Menone76ea262015-04-08 18:23:52 -0500584 "gpio-fan",
585 fan_data,
586 &gpio_fan_cool_ops);
Simon Guinotd6fe1362010-10-22 00:44:19 +0200587
Linus Walleijf9013c12017-09-26 01:09:04 +0200588 dev_info(dev, "GPIO fan initialized\n");
Simon Guinotd6fe1362010-10-22 00:44:19 +0200589
590 return 0;
Simon Guinotd6fe1362010-10-22 00:44:19 +0200591}
592
Nishanth Menonb5cf88e2015-01-08 12:05:03 -0600593static int gpio_fan_remove(struct platform_device *pdev)
Nishanth Menonb95579c2014-12-04 10:58:56 -0600594{
Nishanth Menonb5cf88e2015-01-08 12:05:03 -0600595 struct gpio_fan_data *fan_data = platform_get_drvdata(pdev);
596
597 if (!IS_ERR(fan_data->cdev))
598 thermal_cooling_device_unregister(fan_data->cdev);
Nishanth Menonb95579c2014-12-04 10:58:56 -0600599
600 if (fan_data->ctrl)
601 set_fan_speed(fan_data, 0);
Nishanth Menonb5cf88e2015-01-08 12:05:03 -0600602
603 return 0;
604}
605
606static void gpio_fan_shutdown(struct platform_device *pdev)
607{
608 gpio_fan_remove(pdev);
Nishanth Menonb95579c2014-12-04 10:58:56 -0600609}
610
Rafael J. Wysocki6d20a6c2012-07-08 00:01:03 +0200611#ifdef CONFIG_PM_SLEEP
612static int gpio_fan_suspend(struct device *dev)
Simon Guinotd6fe1362010-10-22 00:44:19 +0200613{
Rafael J. Wysocki6d20a6c2012-07-08 00:01:03 +0200614 struct gpio_fan_data *fan_data = dev_get_drvdata(dev);
Simon Guinotd6fe1362010-10-22 00:44:19 +0200615
616 if (fan_data->ctrl) {
617 fan_data->resume_speed = fan_data->speed_index;
618 set_fan_speed(fan_data, 0);
619 }
620
621 return 0;
622}
623
Rafael J. Wysocki6d20a6c2012-07-08 00:01:03 +0200624static int gpio_fan_resume(struct device *dev)
Simon Guinotd6fe1362010-10-22 00:44:19 +0200625{
Rafael J. Wysocki6d20a6c2012-07-08 00:01:03 +0200626 struct gpio_fan_data *fan_data = dev_get_drvdata(dev);
Simon Guinotd6fe1362010-10-22 00:44:19 +0200627
628 if (fan_data->ctrl)
629 set_fan_speed(fan_data, fan_data->resume_speed);
630
631 return 0;
632}
Rafael J. Wysocki6d20a6c2012-07-08 00:01:03 +0200633
634static SIMPLE_DEV_PM_OPS(gpio_fan_pm, gpio_fan_suspend, gpio_fan_resume);
Guenter Roeck24f9c532013-01-10 05:54:40 -0800635#define GPIO_FAN_PM (&gpio_fan_pm)
Simon Guinotd6fe1362010-10-22 00:44:19 +0200636#else
Rafael J. Wysocki6d20a6c2012-07-08 00:01:03 +0200637#define GPIO_FAN_PM NULL
Simon Guinotd6fe1362010-10-22 00:44:19 +0200638#endif
639
640static struct platform_driver gpio_fan_driver = {
641 .probe = gpio_fan_probe,
Nishanth Menonb5cf88e2015-01-08 12:05:03 -0600642 .remove = gpio_fan_remove,
Nishanth Menonb95579c2014-12-04 10:58:56 -0600643 .shutdown = gpio_fan_shutdown,
Simon Guinotd6fe1362010-10-22 00:44:19 +0200644 .driver = {
645 .name = "gpio-fan",
Rafael J. Wysocki6d20a6c2012-07-08 00:01:03 +0200646 .pm = GPIO_FAN_PM,
Jamie Lentin55fb8b062012-09-14 17:07:06 +0100647 .of_match_table = of_match_ptr(of_gpio_fan_match),
Simon Guinotd6fe1362010-10-22 00:44:19 +0200648 },
649};
650
Axel Lin25a236a2011-11-25 02:31:00 -0500651module_platform_driver(gpio_fan_driver);
Simon Guinotd6fe1362010-10-22 00:44:19 +0200652
653MODULE_AUTHOR("Simon Guinot <sguinot@lacie.com>");
654MODULE_DESCRIPTION("GPIO FAN driver");
655MODULE_LICENSE("GPL");
656MODULE_ALIAS("platform:gpio-fan");