Blame view

drivers/hwmon/gpio-fan.c 13.9 KB
1a59d1b8e   Thomas Gleixner   treewide: Replace...
1
  // SPDX-License-Identifier: GPL-2.0-or-later
d6fe1360f   Simon Guinot   hwmon: add generi...
2
3
4
5
6
7
  /*
   * gpio-fan.c - Hwmon driver for fans connected to GPIO lines.
   *
   * Copyright (C) 2010 LaCie
   *
   * Author: Simon Guinot <sguinot@lacie.com>
d6fe1360f   Simon Guinot   hwmon: add generi...
8
9
10
11
12
13
14
15
16
17
18
   */
  
  #include <linux/module.h>
  #include <linux/init.h>
  #include <linux/slab.h>
  #include <linux/interrupt.h>
  #include <linux/irq.h>
  #include <linux/platform_device.h>
  #include <linux/err.h>
  #include <linux/mutex.h>
  #include <linux/hwmon.h>
9de382fdd   Linus Walleij   hwmon: (gpio-fan)...
19
  #include <linux/gpio/consumer.h>
c50588aba   Sachin Kamat   hwmon: (gpio-fan)...
20
  #include <linux/of.h>
55fb8b068   Jamie Lentin   hwmon: Add device...
21
  #include <linux/of_platform.h>
b5cf88e46   Nishanth Menon   (gpio-fan): Add t...
22
  #include <linux/thermal.h>
d6fe1360f   Simon Guinot   hwmon: add generi...
23

ef7a61241   Linus Walleij   hwmon: (gpio-fan)...
24
25
26
27
  struct gpio_fan_speed {
  	int rpm;
  	int ctrl_val;
  };
d6fe1360f   Simon Guinot   hwmon: add generi...
28
  struct gpio_fan_data {
8c0eb9bc5   Linus Walleij   hwmon: (gpio-fan)...
29
  	struct device		*dev;
d6fe1360f   Simon Guinot   hwmon: add generi...
30
  	struct device		*hwmon_dev;
b5cf88e46   Nishanth Menon   (gpio-fan): Add t...
31
32
  	/* Cooling device if any */
  	struct thermal_cooling_device *cdev;
d6fe1360f   Simon Guinot   hwmon: add generi...
33
  	struct mutex		lock; /* lock GPIOs operations. */
e99c2e5d6   Linus Walleij   hwmon: (gpio-fan)...
34
  	int			num_gpios;
9de382fdd   Linus Walleij   hwmon: (gpio-fan)...
35
  	struct gpio_desc	**gpios;
d6fe1360f   Simon Guinot   hwmon: add generi...
36
37
38
  	int			num_speed;
  	struct gpio_fan_speed	*speed;
  	int			speed_index;
6d20a6c06   Rafael J. Wysocki   hwmon: (gpio-fan)...
39
  #ifdef CONFIG_PM_SLEEP
d6fe1360f   Simon Guinot   hwmon: add generi...
40
41
42
  	int			resume_speed;
  #endif
  	bool			pwm_enable;
9de382fdd   Linus Walleij   hwmon: (gpio-fan)...
43
  	struct gpio_desc	*alarm_gpio;
d6fe1360f   Simon Guinot   hwmon: add generi...
44
45
46
47
48
49
50
51
52
53
54
  	struct work_struct	alarm_work;
  };
  
  /*
   * Alarm GPIO.
   */
  
  static void fan_alarm_notify(struct work_struct *ws)
  {
  	struct gpio_fan_data *fan_data =
  		container_of(ws, struct gpio_fan_data, alarm_work);
277c628fa   Christian Schneider   hwmon: (gpio-fan)...
55
56
  	sysfs_notify(&fan_data->hwmon_dev->kobj, NULL, "fan1_alarm");
  	kobject_uevent(&fan_data->hwmon_dev->kobj, KOBJ_CHANGE);
d6fe1360f   Simon Guinot   hwmon: add generi...
57
58
59
60
61
62
63
64
65
66
  }
  
  static irqreturn_t fan_alarm_irq_handler(int irq, void *dev_id)
  {
  	struct gpio_fan_data *fan_data = dev_id;
  
  	schedule_work(&fan_data->alarm_work);
  
  	return IRQ_NONE;
  }
c490c63e9   Julia Lawall   hwmon: (gpio-fan)...
67
68
  static ssize_t fan1_alarm_show(struct device *dev,
  			       struct device_attribute *attr, char *buf)
d6fe1360f   Simon Guinot   hwmon: add generi...
69
70
  {
  	struct gpio_fan_data *fan_data = dev_get_drvdata(dev);
d6fe1360f   Simon Guinot   hwmon: add generi...
71

9de382fdd   Linus Walleij   hwmon: (gpio-fan)...
72
73
74
  	return sprintf(buf, "%d
  ",
  		       gpiod_get_value_cansleep(fan_data->alarm_gpio));
d6fe1360f   Simon Guinot   hwmon: add generi...
75
  }
c490c63e9   Julia Lawall   hwmon: (gpio-fan)...
76
  static DEVICE_ATTR_RO(fan1_alarm);
d6fe1360f   Simon Guinot   hwmon: add generi...
77

b5482f7e6   Linus Walleij   hwmon: (gpio-fan)...
78
  static int fan_alarm_init(struct gpio_fan_data *fan_data)
d6fe1360f   Simon Guinot   hwmon: add generi...
79
  {
d6fe1360f   Simon Guinot   hwmon: add generi...
80
  	int alarm_irq;
8c0eb9bc5   Linus Walleij   hwmon: (gpio-fan)...
81
  	struct device *dev = fan_data->dev;
d6fe1360f   Simon Guinot   hwmon: add generi...
82

d6fe1360f   Simon Guinot   hwmon: add generi...
83
84
85
86
  	/*
  	 * If the alarm GPIO don't support interrupts, just leave
  	 * without initializing the fail notification support.
  	 */
9de382fdd   Linus Walleij   hwmon: (gpio-fan)...
87
88
  	alarm_irq = gpiod_to_irq(fan_data->alarm_gpio);
  	if (alarm_irq <= 0)
d6fe1360f   Simon Guinot   hwmon: add generi...
89
90
91
  		return 0;
  
  	INIT_WORK(&fan_data->alarm_work, fan_alarm_notify);
dced35aeb   Thomas Gleixner   drivers: Final ir...
92
  	irq_set_irq_type(alarm_irq, IRQ_TYPE_EDGE_BOTH);
9de382fdd   Linus Walleij   hwmon: (gpio-fan)...
93
94
  	return devm_request_irq(dev, alarm_irq, fan_alarm_irq_handler,
  				IRQF_SHARED, "GPIO fan alarm", fan_data);
d6fe1360f   Simon Guinot   hwmon: add generi...
95
  }
d6fe1360f   Simon Guinot   hwmon: add generi...
96
97
98
99
100
101
102
103
  /*
   * Control GPIOs.
   */
  
  /* Must be called with fan_data->lock held, except during initialization. */
  static void __set_fan_ctrl(struct gpio_fan_data *fan_data, int ctrl_val)
  {
  	int i;
e99c2e5d6   Linus Walleij   hwmon: (gpio-fan)...
104
  	for (i = 0; i < fan_data->num_gpios; i++)
9de382fdd   Linus Walleij   hwmon: (gpio-fan)...
105
106
  		gpiod_set_value_cansleep(fan_data->gpios[i],
  					 (ctrl_val >> i) & 1);
d6fe1360f   Simon Guinot   hwmon: add generi...
107
108
109
110
111
112
  }
  
  static int __get_fan_ctrl(struct gpio_fan_data *fan_data)
  {
  	int i;
  	int ctrl_val = 0;
e99c2e5d6   Linus Walleij   hwmon: (gpio-fan)...
113
  	for (i = 0; i < fan_data->num_gpios; i++) {
d6fe1360f   Simon Guinot   hwmon: add generi...
114
  		int value;
9de382fdd   Linus Walleij   hwmon: (gpio-fan)...
115
  		value = gpiod_get_value_cansleep(fan_data->gpios[i]);
d6fe1360f   Simon Guinot   hwmon: add generi...
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
  		ctrl_val |= (value << i);
  	}
  	return ctrl_val;
  }
  
  /* Must be called with fan_data->lock held, except during initialization. */
  static void set_fan_speed(struct gpio_fan_data *fan_data, int speed_index)
  {
  	if (fan_data->speed_index == speed_index)
  		return;
  
  	__set_fan_ctrl(fan_data, fan_data->speed[speed_index].ctrl_val);
  	fan_data->speed_index = speed_index;
  }
  
  static int get_fan_speed_index(struct gpio_fan_data *fan_data)
  {
  	int ctrl_val = __get_fan_ctrl(fan_data);
  	int i;
  
  	for (i = 0; i < fan_data->num_speed; i++)
  		if (fan_data->speed[i].ctrl_val == ctrl_val)
  			return i;
8c0eb9bc5   Linus Walleij   hwmon: (gpio-fan)...
139
  	dev_warn(fan_data->dev,
d6fe1360f   Simon Guinot   hwmon: add generi...
140
141
  		 "missing speed array entry for GPIO value 0x%x
  ", ctrl_val);
c52ae3d27   Guenter Roeck   hwmon: (gpio_fan)...
142
  	return -ENODEV;
d6fe1360f   Simon Guinot   hwmon: add generi...
143
  }
2565fb05d   Axel Lin   hwmon: (gpio-fan)...
144
  static int rpm_to_speed_index(struct gpio_fan_data *fan_data, unsigned long rpm)
d6fe1360f   Simon Guinot   hwmon: add generi...
145
146
147
148
149
150
151
152
153
154
  {
  	struct gpio_fan_speed *speed = fan_data->speed;
  	int i;
  
  	for (i = 0; i < fan_data->num_speed; i++)
  		if (speed[i].rpm >= rpm)
  			return i;
  
  	return fan_data->num_speed - 1;
  }
c490c63e9   Julia Lawall   hwmon: (gpio-fan)...
155
156
  static ssize_t pwm1_show(struct device *dev, struct device_attribute *attr,
  			 char *buf)
d6fe1360f   Simon Guinot   hwmon: add generi...
157
158
159
160
161
162
163
  {
  	struct gpio_fan_data *fan_data = dev_get_drvdata(dev);
  	u8 pwm = fan_data->speed_index * 255 / (fan_data->num_speed - 1);
  
  	return sprintf(buf, "%d
  ", pwm);
  }
c490c63e9   Julia Lawall   hwmon: (gpio-fan)...
164
165
  static ssize_t pwm1_store(struct device *dev, struct device_attribute *attr,
  			  const char *buf, size_t count)
d6fe1360f   Simon Guinot   hwmon: add generi...
166
167
168
169
170
  {
  	struct gpio_fan_data *fan_data = dev_get_drvdata(dev);
  	unsigned long pwm;
  	int speed_index;
  	int ret = count;
179c4fdb5   Frans Meulenbroeks   hwmon: replaced s...
171
  	if (kstrtoul(buf, 10, &pwm) || pwm > 255)
d6fe1360f   Simon Guinot   hwmon: add generi...
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
  		return -EINVAL;
  
  	mutex_lock(&fan_data->lock);
  
  	if (!fan_data->pwm_enable) {
  		ret = -EPERM;
  		goto exit_unlock;
  	}
  
  	speed_index = DIV_ROUND_UP(pwm * (fan_data->num_speed - 1), 255);
  	set_fan_speed(fan_data, speed_index);
  
  exit_unlock:
  	mutex_unlock(&fan_data->lock);
  
  	return ret;
  }
c490c63e9   Julia Lawall   hwmon: (gpio-fan)...
189
190
  static ssize_t pwm1_enable_show(struct device *dev,
  				struct device_attribute *attr, char *buf)
d6fe1360f   Simon Guinot   hwmon: add generi...
191
192
193
194
195
196
  {
  	struct gpio_fan_data *fan_data = dev_get_drvdata(dev);
  
  	return sprintf(buf, "%d
  ", fan_data->pwm_enable);
  }
c490c63e9   Julia Lawall   hwmon: (gpio-fan)...
197
198
199
  static ssize_t pwm1_enable_store(struct device *dev,
  				 struct device_attribute *attr,
  				 const char *buf, size_t count)
d6fe1360f   Simon Guinot   hwmon: add generi...
200
201
202
  {
  	struct gpio_fan_data *fan_data = dev_get_drvdata(dev);
  	unsigned long val;
179c4fdb5   Frans Meulenbroeks   hwmon: replaced s...
203
  	if (kstrtoul(buf, 10, &val) || val > 1)
d6fe1360f   Simon Guinot   hwmon: add generi...
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
  		return -EINVAL;
  
  	if (fan_data->pwm_enable == val)
  		return count;
  
  	mutex_lock(&fan_data->lock);
  
  	fan_data->pwm_enable = val;
  
  	/* Disable manual control mode: set fan at full speed. */
  	if (val == 0)
  		set_fan_speed(fan_data, fan_data->num_speed - 1);
  
  	mutex_unlock(&fan_data->lock);
  
  	return count;
  }
c490c63e9   Julia Lawall   hwmon: (gpio-fan)...
221
222
  static ssize_t pwm1_mode_show(struct device *dev,
  			      struct device_attribute *attr, char *buf)
d6fe1360f   Simon Guinot   hwmon: add generi...
223
224
225
226
  {
  	return sprintf(buf, "0
  ");
  }
c490c63e9   Julia Lawall   hwmon: (gpio-fan)...
227
228
  static ssize_t fan1_min_show(struct device *dev,
  			     struct device_attribute *attr, char *buf)
d6fe1360f   Simon Guinot   hwmon: add generi...
229
230
231
232
233
234
  {
  	struct gpio_fan_data *fan_data = dev_get_drvdata(dev);
  
  	return sprintf(buf, "%d
  ", fan_data->speed[0].rpm);
  }
c490c63e9   Julia Lawall   hwmon: (gpio-fan)...
235
236
  static ssize_t fan1_max_show(struct device *dev,
  			     struct device_attribute *attr, char *buf)
d6fe1360f   Simon Guinot   hwmon: add generi...
237
238
239
240
241
242
243
  {
  	struct gpio_fan_data *fan_data = dev_get_drvdata(dev);
  
  	return sprintf(buf, "%d
  ",
  		       fan_data->speed[fan_data->num_speed - 1].rpm);
  }
c490c63e9   Julia Lawall   hwmon: (gpio-fan)...
244
245
  static ssize_t fan1_input_show(struct device *dev,
  			       struct device_attribute *attr, char *buf)
d6fe1360f   Simon Guinot   hwmon: add generi...
246
247
248
249
250
251
252
253
254
255
256
257
258
  {
  	struct gpio_fan_data *fan_data = dev_get_drvdata(dev);
  
  	return sprintf(buf, "%d
  ", fan_data->speed[fan_data->speed_index].rpm);
  }
  
  static ssize_t set_rpm(struct device *dev, struct device_attribute *attr,
  		       const char *buf, size_t count)
  {
  	struct gpio_fan_data *fan_data = dev_get_drvdata(dev);
  	unsigned long rpm;
  	int ret = count;
179c4fdb5   Frans Meulenbroeks   hwmon: replaced s...
259
  	if (kstrtoul(buf, 10, &rpm))
d6fe1360f   Simon Guinot   hwmon: add generi...
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
  		return -EINVAL;
  
  	mutex_lock(&fan_data->lock);
  
  	if (!fan_data->pwm_enable) {
  		ret = -EPERM;
  		goto exit_unlock;
  	}
  
  	set_fan_speed(fan_data, rpm_to_speed_index(fan_data, rpm));
  
  exit_unlock:
  	mutex_unlock(&fan_data->lock);
  
  	return ret;
  }
c490c63e9   Julia Lawall   hwmon: (gpio-fan)...
276
277
278
279
280
281
  static DEVICE_ATTR_RW(pwm1);
  static DEVICE_ATTR_RW(pwm1_enable);
  static DEVICE_ATTR_RO(pwm1_mode);
  static DEVICE_ATTR_RO(fan1_min);
  static DEVICE_ATTR_RO(fan1_max);
  static DEVICE_ATTR_RO(fan1_input);
905bc0d46   Guenter Roeck   hwmon: (gpio-fan)...
282
  static DEVICE_ATTR(fan1_target, 0644, fan1_input_show, set_rpm);
d6fe1360f   Simon Guinot   hwmon: add generi...
283

c81cc5a4c   Guenter Roeck   hwmon: (gpio-fan)...
284
285
286
287
288
  static umode_t gpio_fan_is_visible(struct kobject *kobj,
  				   struct attribute *attr, int index)
  {
  	struct device *dev = container_of(kobj, struct device, kobj);
  	struct gpio_fan_data *data = dev_get_drvdata(dev);
c9933cb16   Linus Walleij   hwmon: (gpio-fan)...
289
  	if (index == 0 && !data->alarm_gpio)
c81cc5a4c   Guenter Roeck   hwmon: (gpio-fan)...
290
  		return 0;
e99c2e5d6   Linus Walleij   hwmon: (gpio-fan)...
291
  	if (index > 0 && !data->gpios)
c81cc5a4c   Guenter Roeck   hwmon: (gpio-fan)...
292
293
294
295
296
297
  		return 0;
  
  	return attr->mode;
  }
  
  static struct attribute *gpio_fan_attributes[] = {
7258a1253   Guenter Roeck   hwmon: (gpio-fan)...
298
299
  	&dev_attr_fan1_alarm.attr,		/* 0 */
  	&dev_attr_pwm1.attr,			/* 1 */
d6fe1360f   Simon Guinot   hwmon: add generi...
300
301
302
303
304
305
306
307
  	&dev_attr_pwm1_enable.attr,
  	&dev_attr_pwm1_mode.attr,
  	&dev_attr_fan1_input.attr,
  	&dev_attr_fan1_target.attr,
  	&dev_attr_fan1_min.attr,
  	&dev_attr_fan1_max.attr,
  	NULL
  };
c81cc5a4c   Guenter Roeck   hwmon: (gpio-fan)...
308
309
310
  static const struct attribute_group gpio_fan_group = {
  	.attrs = gpio_fan_attributes,
  	.is_visible = gpio_fan_is_visible,
d6fe1360f   Simon Guinot   hwmon: add generi...
311
  };
7258a1253   Guenter Roeck   hwmon: (gpio-fan)...
312
313
314
315
  static const struct attribute_group *gpio_fan_groups[] = {
  	&gpio_fan_group,
  	NULL
  };
b5482f7e6   Linus Walleij   hwmon: (gpio-fan)...
316
  static int fan_ctrl_init(struct gpio_fan_data *fan_data)
d6fe1360f   Simon Guinot   hwmon: add generi...
317
  {
e99c2e5d6   Linus Walleij   hwmon: (gpio-fan)...
318
  	int num_gpios = fan_data->num_gpios;
9de382fdd   Linus Walleij   hwmon: (gpio-fan)...
319
  	struct gpio_desc **gpios = fan_data->gpios;
d6fe1360f   Simon Guinot   hwmon: add generi...
320
  	int i, err;
e99c2e5d6   Linus Walleij   hwmon: (gpio-fan)...
321
  	for (i = 0; i < num_gpios; i++) {
9de382fdd   Linus Walleij   hwmon: (gpio-fan)...
322
323
324
325
326
327
328
329
  		/*
  		 * The GPIO descriptors were retrieved with GPIOD_ASIS so here
  		 * we set the GPIO into output mode, carefully preserving the
  		 * current value by setting it to whatever it is already set
  		 * (no surprise changes in default fan speed).
  		 */
  		err = gpiod_direction_output(gpios[i],
  					gpiod_get_value_cansleep(gpios[i]));
d00985f3d   Guenter Roeck   hwmon: (gpio-fan)...
330
331
  		if (err)
  			return err;
d6fe1360f   Simon Guinot   hwmon: add generi...
332
  	}
d6fe1360f   Simon Guinot   hwmon: add generi...
333
334
  	fan_data->pwm_enable = true; /* Enable manual fan speed control. */
  	fan_data->speed_index = get_fan_speed_index(fan_data);
d00985f3d   Guenter Roeck   hwmon: (gpio-fan)...
335
  	if (fan_data->speed_index < 0)
c52ae3d27   Guenter Roeck   hwmon: (gpio_fan)...
336
  		return fan_data->speed_index;
d6fe1360f   Simon Guinot   hwmon: add generi...
337

c81cc5a4c   Guenter Roeck   hwmon: (gpio-fan)...
338
  	return 0;
d6fe1360f   Simon Guinot   hwmon: add generi...
339
  }
b5cf88e46   Nishanth Menon   (gpio-fan): Add t...
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
  static int gpio_fan_get_max_state(struct thermal_cooling_device *cdev,
  				  unsigned long *state)
  {
  	struct gpio_fan_data *fan_data = cdev->devdata;
  
  	if (!fan_data)
  		return -EINVAL;
  
  	*state = fan_data->num_speed - 1;
  	return 0;
  }
  
  static int gpio_fan_get_cur_state(struct thermal_cooling_device *cdev,
  				  unsigned long *state)
  {
  	struct gpio_fan_data *fan_data = cdev->devdata;
b5cf88e46   Nishanth Menon   (gpio-fan): Add t...
356
357
358
  
  	if (!fan_data)
  		return -EINVAL;
000e09491   Nishanth Menon   hwmon: (gpio-fan)...
359
  	*state = fan_data->speed_index;
b5cf88e46   Nishanth Menon   (gpio-fan): Add t...
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
  	return 0;
  }
  
  static int gpio_fan_set_cur_state(struct thermal_cooling_device *cdev,
  				  unsigned long state)
  {
  	struct gpio_fan_data *fan_data = cdev->devdata;
  
  	if (!fan_data)
  		return -EINVAL;
  
  	set_fan_speed(fan_data, state);
  	return 0;
  }
  
  static const struct thermal_cooling_device_ops gpio_fan_cool_ops = {
  	.get_max_state = gpio_fan_get_max_state,
  	.get_cur_state = gpio_fan_get_cur_state,
  	.set_cur_state = gpio_fan_set_cur_state,
  };
55fb8b068   Jamie Lentin   hwmon: Add device...
380
381
382
  /*
   * Translate OpenFirmware node properties into platform_data
   */
b5482f7e6   Linus Walleij   hwmon: (gpio-fan)...
383
  static int gpio_fan_get_of_data(struct gpio_fan_data *fan_data)
55fb8b068   Jamie Lentin   hwmon: Add device...
384
  {
55fb8b068   Jamie Lentin   hwmon: Add device...
385
  	struct gpio_fan_speed *speed;
b5482f7e6   Linus Walleij   hwmon: (gpio-fan)...
386
387
  	struct device *dev = fan_data->dev;
  	struct device_node *np = dev->of_node;
9de382fdd   Linus Walleij   hwmon: (gpio-fan)...
388
  	struct gpio_desc **gpios;
55fb8b068   Jamie Lentin   hwmon: Add device...
389
390
391
392
  	unsigned i;
  	u32 u;
  	struct property *prop;
  	const __be32 *p;
73ef85f42   Simon Guinot   hwmon: (gpio-fan)...
393
  	/* Alarm GPIO if one exists */
9de382fdd   Linus Walleij   hwmon: (gpio-fan)...
394
395
396
  	fan_data->alarm_gpio = devm_gpiod_get_optional(dev, "alarm", GPIOD_IN);
  	if (IS_ERR(fan_data->alarm_gpio))
  		return PTR_ERR(fan_data->alarm_gpio);
73ef85f42   Simon Guinot   hwmon: (gpio-fan)...
397

55fb8b068   Jamie Lentin   hwmon: Add device...
398
  	/* Fill GPIO pin array */
9de382fdd   Linus Walleij   hwmon: (gpio-fan)...
399
  	fan_data->num_gpios = gpiod_count(dev, NULL);
e99c2e5d6   Linus Walleij   hwmon: (gpio-fan)...
400
  	if (fan_data->num_gpios <= 0) {
c9933cb16   Linus Walleij   hwmon: (gpio-fan)...
401
  		if (fan_data->alarm_gpio)
73ef85f42   Simon Guinot   hwmon: (gpio-fan)...
402
403
  			return 0;
  		dev_err(dev, "DT properties empty / missing");
55fb8b068   Jamie Lentin   hwmon: Add device...
404
405
  		return -ENODEV;
  	}
a86854d0c   Kees Cook   treewide: devm_kz...
406
407
  	gpios = devm_kcalloc(dev,
  			     fan_data->num_gpios, sizeof(struct gpio_desc *),
9de382fdd   Linus Walleij   hwmon: (gpio-fan)...
408
  			     GFP_KERNEL);
e99c2e5d6   Linus Walleij   hwmon: (gpio-fan)...
409
  	if (!gpios)
55fb8b068   Jamie Lentin   hwmon: Add device...
410
  		return -ENOMEM;
e99c2e5d6   Linus Walleij   hwmon: (gpio-fan)...
411
  	for (i = 0; i < fan_data->num_gpios; i++) {
9de382fdd   Linus Walleij   hwmon: (gpio-fan)...
412
413
414
  		gpios[i] = devm_gpiod_get_index(dev, NULL, i, GPIOD_ASIS);
  		if (IS_ERR(gpios[i]))
  			return PTR_ERR(gpios[i]);
55fb8b068   Jamie Lentin   hwmon: Add device...
415
  	}
e99c2e5d6   Linus Walleij   hwmon: (gpio-fan)...
416
  	fan_data->gpios = gpios;
55fb8b068   Jamie Lentin   hwmon: Add device...
417
418
  
  	/* Get number of RPM/ctrl_val pairs in speed map */
b5482f7e6   Linus Walleij   hwmon: (gpio-fan)...
419
  	prop = of_find_property(np, "gpio-fan,speed-map", &i);
55fb8b068   Jamie Lentin   hwmon: Add device...
420
421
422
423
424
425
426
427
428
  	if (!prop) {
  		dev_err(dev, "gpio-fan,speed-map DT property missing");
  		return -ENODEV;
  	}
  	i = i / sizeof(u32);
  	if (i == 0 || i & 1) {
  		dev_err(dev, "gpio-fan,speed-map contains zero/odd number of entries");
  		return -ENODEV;
  	}
b5482f7e6   Linus Walleij   hwmon: (gpio-fan)...
429
  	fan_data->num_speed = i / 2;
55fb8b068   Jamie Lentin   hwmon: Add device...
430
431
432
433
434
435
  
  	/*
  	 * Populate speed map
  	 * Speed map is in the form <RPM ctrl_val RPM ctrl_val ...>
  	 * this needs splitting into pairs to create gpio_fan_speed structs
  	 */
a86854d0c   Kees Cook   treewide: devm_kz...
436
437
  	speed = devm_kcalloc(dev,
  			fan_data->num_speed, sizeof(struct gpio_fan_speed),
55fb8b068   Jamie Lentin   hwmon: Add device...
438
439
440
441
  			GFP_KERNEL);
  	if (!speed)
  		return -ENOMEM;
  	p = NULL;
b5482f7e6   Linus Walleij   hwmon: (gpio-fan)...
442
  	for (i = 0; i < fan_data->num_speed; i++) {
55fb8b068   Jamie Lentin   hwmon: Add device...
443
444
445
446
447
448
449
450
451
  		p = of_prop_next_u32(prop, p, &u);
  		if (!p)
  			return -ENODEV;
  		speed[i].rpm = u;
  		p = of_prop_next_u32(prop, p, &u);
  		if (!p)
  			return -ENODEV;
  		speed[i].ctrl_val = u;
  	}
b5482f7e6   Linus Walleij   hwmon: (gpio-fan)...
452
  	fan_data->speed = speed;
55fb8b068   Jamie Lentin   hwmon: Add device...
453

55fb8b068   Jamie Lentin   hwmon: Add device...
454
455
  	return 0;
  }
6de709c5e   Jingoo Han   hwmon: (gpio-fan)...
456
  static const struct of_device_id of_gpio_fan_match[] = {
55fb8b068   Jamie Lentin   hwmon: Add device...
457
458
459
  	{ .compatible = "gpio-fan", },
  	{},
  };
fe5152882   Luis de Bethencourt   hwmon: (gpio-fan)...
460
  MODULE_DEVICE_TABLE(of, of_gpio_fan_match);
55fb8b068   Jamie Lentin   hwmon: Add device...
461

953478455   Guenter Roeck   hwmon: (gpio-fan)...
462
463
464
465
  static void gpio_fan_stop(void *data)
  {
  	set_fan_speed(data, 0);
  }
6c931ae1c   Bill Pemberton   hwmon: remove use...
466
  static int gpio_fan_probe(struct platform_device *pdev)
d6fe1360f   Simon Guinot   hwmon: add generi...
467
468
469
  {
  	int err;
  	struct gpio_fan_data *fan_data;
f9013c167   Linus Walleij   hwmon: (gpio-fan)...
470
471
  	struct device *dev = &pdev->dev;
  	struct device_node *np = dev->of_node;
d6fe1360f   Simon Guinot   hwmon: add generi...
472

f9013c167   Linus Walleij   hwmon: (gpio-fan)...
473
  	fan_data = devm_kzalloc(dev, sizeof(struct gpio_fan_data),
b5cf88e46   Nishanth Menon   (gpio-fan): Add t...
474
475
476
  				GFP_KERNEL);
  	if (!fan_data)
  		return -ENOMEM;
534e28d87   Miquel Raynal   hwmon: (gpio-fan)...
477
  	fan_data->dev = dev;
b5482f7e6   Linus Walleij   hwmon: (gpio-fan)...
478
  	err = gpio_fan_get_of_data(fan_data);
a9b4c8afc   Linus Walleij   hwmon: (gpio-fan)...
479
480
  	if (err)
  		return err;
d6fe1360f   Simon Guinot   hwmon: add generi...
481

d6fe1360f   Simon Guinot   hwmon: add generi...
482
483
  	platform_set_drvdata(pdev, fan_data);
  	mutex_init(&fan_data->lock);
d6fe1360f   Simon Guinot   hwmon: add generi...
484
  	/* Configure control GPIOs if available. */
e99c2e5d6   Linus Walleij   hwmon: (gpio-fan)...
485
  	if (fan_data->gpios && fan_data->num_gpios > 0) {
b5482f7e6   Linus Walleij   hwmon: (gpio-fan)...
486
  		if (!fan_data->speed || fan_data->num_speed <= 1)
c81cc5a4c   Guenter Roeck   hwmon: (gpio-fan)...
487
  			return -EINVAL;
b5482f7e6   Linus Walleij   hwmon: (gpio-fan)...
488
  		err = fan_ctrl_init(fan_data);
d6fe1360f   Simon Guinot   hwmon: add generi...
489
  		if (err)
c81cc5a4c   Guenter Roeck   hwmon: (gpio-fan)...
490
  			return err;
b9bb92e1d   Guenter Roeck   hwmon: (gpio-fan)...
491
492
493
  		err = devm_add_action_or_reset(dev, gpio_fan_stop, fan_data);
  		if (err)
  			return err;
d6fe1360f   Simon Guinot   hwmon: add generi...
494
  	}
d6fe1360f   Simon Guinot   hwmon: add generi...
495
  	/* Make this driver part of hwmon class. */
49153b092   Axel Lin   hwmon: (gpio-fan)...
496
  	fan_data->hwmon_dev =
f9013c167   Linus Walleij   hwmon: (gpio-fan)...
497
  		devm_hwmon_device_register_with_groups(dev,
49153b092   Axel Lin   hwmon: (gpio-fan)...
498
499
  						       "gpio_fan", fan_data,
  						       gpio_fan_groups);
7258a1253   Guenter Roeck   hwmon: (gpio-fan)...
500
501
  	if (IS_ERR(fan_data->hwmon_dev))
  		return PTR_ERR(fan_data->hwmon_dev);
a9b4c8afc   Linus Walleij   hwmon: (gpio-fan)...
502

f2173fa22   Christian Schneider   hwmon: (gpio-fan)...
503
504
505
506
507
508
  	/* Configure alarm GPIO if available. */
  	if (fan_data->alarm_gpio) {
  		err = fan_alarm_init(fan_data);
  		if (err)
  			return err;
  	}
e76ea2614   Nishanth Menon   hwmon: (gpio-fan)...
509
  	/* Optional cooling device register for Device tree platforms */
953478455   Guenter Roeck   hwmon: (gpio-fan)...
510
511
  	fan_data->cdev = devm_thermal_of_cooling_device_register(dev, np,
  				"gpio-fan", fan_data, &gpio_fan_cool_ops);
d6fe1360f   Simon Guinot   hwmon: add generi...
512

f9013c167   Linus Walleij   hwmon: (gpio-fan)...
513
514
  	dev_info(dev, "GPIO fan initialized
  ");
d6fe1360f   Simon Guinot   hwmon: add generi...
515
516
  
  	return 0;
d6fe1360f   Simon Guinot   hwmon: add generi...
517
  }
953478455   Guenter Roeck   hwmon: (gpio-fan)...
518
  static void gpio_fan_shutdown(struct platform_device *pdev)
b95579cd8   Nishanth Menon   hwmon: (gpio-fan)...
519
  {
b5cf88e46   Nishanth Menon   (gpio-fan): Add t...
520
  	struct gpio_fan_data *fan_data = platform_get_drvdata(pdev);
e99c2e5d6   Linus Walleij   hwmon: (gpio-fan)...
521
  	if (fan_data->gpios)
b95579cd8   Nishanth Menon   hwmon: (gpio-fan)...
522
523
  		set_fan_speed(fan_data, 0);
  }
6d20a6c06   Rafael J. Wysocki   hwmon: (gpio-fan)...
524
525
  #ifdef CONFIG_PM_SLEEP
  static int gpio_fan_suspend(struct device *dev)
d6fe1360f   Simon Guinot   hwmon: add generi...
526
  {
6d20a6c06   Rafael J. Wysocki   hwmon: (gpio-fan)...
527
  	struct gpio_fan_data *fan_data = dev_get_drvdata(dev);
d6fe1360f   Simon Guinot   hwmon: add generi...
528

e99c2e5d6   Linus Walleij   hwmon: (gpio-fan)...
529
  	if (fan_data->gpios) {
d6fe1360f   Simon Guinot   hwmon: add generi...
530
531
532
533
534
535
  		fan_data->resume_speed = fan_data->speed_index;
  		set_fan_speed(fan_data, 0);
  	}
  
  	return 0;
  }
6d20a6c06   Rafael J. Wysocki   hwmon: (gpio-fan)...
536
  static int gpio_fan_resume(struct device *dev)
d6fe1360f   Simon Guinot   hwmon: add generi...
537
  {
6d20a6c06   Rafael J. Wysocki   hwmon: (gpio-fan)...
538
  	struct gpio_fan_data *fan_data = dev_get_drvdata(dev);
d6fe1360f   Simon Guinot   hwmon: add generi...
539

e99c2e5d6   Linus Walleij   hwmon: (gpio-fan)...
540
  	if (fan_data->gpios)
d6fe1360f   Simon Guinot   hwmon: add generi...
541
542
543
544
  		set_fan_speed(fan_data, fan_data->resume_speed);
  
  	return 0;
  }
6d20a6c06   Rafael J. Wysocki   hwmon: (gpio-fan)...
545
546
  
  static SIMPLE_DEV_PM_OPS(gpio_fan_pm, gpio_fan_suspend, gpio_fan_resume);
24f9c539b   Guenter Roeck   hwmon: Fix 'Macro...
547
  #define GPIO_FAN_PM	(&gpio_fan_pm)
d6fe1360f   Simon Guinot   hwmon: add generi...
548
  #else
6d20a6c06   Rafael J. Wysocki   hwmon: (gpio-fan)...
549
  #define GPIO_FAN_PM	NULL
d6fe1360f   Simon Guinot   hwmon: add generi...
550
551
552
553
  #endif
  
  static struct platform_driver gpio_fan_driver = {
  	.probe		= gpio_fan_probe,
b95579cd8   Nishanth Menon   hwmon: (gpio-fan)...
554
  	.shutdown	= gpio_fan_shutdown,
d6fe1360f   Simon Guinot   hwmon: add generi...
555
556
  	.driver	= {
  		.name	= "gpio-fan",
6d20a6c06   Rafael J. Wysocki   hwmon: (gpio-fan)...
557
  		.pm	= GPIO_FAN_PM,
55fb8b068   Jamie Lentin   hwmon: Add device...
558
  		.of_match_table = of_match_ptr(of_gpio_fan_match),
d6fe1360f   Simon Guinot   hwmon: add generi...
559
560
  	},
  };
25a236a5d   Axel Lin   hwmon: convert dr...
561
  module_platform_driver(gpio_fan_driver);
d6fe1360f   Simon Guinot   hwmon: add generi...
562
563
564
565
566
  
  MODULE_AUTHOR("Simon Guinot <sguinot@lacie.com>");
  MODULE_DESCRIPTION("GPIO FAN driver");
  MODULE_LICENSE("GPL");
  MODULE_ALIAS("platform:gpio-fan");