Blame view

drivers/hwmon/pwm-fan.c 10.7 KB
c942fddf8   Thomas Gleixner   treewide: Replace...
1
  // SPDX-License-Identifier: GPL-2.0-or-later
d82d57767   Kamil Debski   hwmon: Add pwm-fa...
2
3
4
5
6
7
  /*
   * pwm-fan.c - Hwmon driver for fans connected to PWM lines.
   *
   * Copyright (c) 2014 Samsung Electronics Co., Ltd.
   *
   * Author: Kamil Debski <k.debski@samsung.com>
d82d57767   Kamil Debski   hwmon: Add pwm-fa...
8
9
10
11
   */
  
  #include <linux/hwmon.h>
  #include <linux/hwmon-sysfs.h>
6b1ec4789   Stefan Wahren   hwmon: (pwm-fan) ...
12
  #include <linux/interrupt.h>
d82d57767   Kamil Debski   hwmon: Add pwm-fa...
13
14
15
16
17
  #include <linux/module.h>
  #include <linux/mutex.h>
  #include <linux/of.h>
  #include <linux/platform_device.h>
  #include <linux/pwm.h>
b57e1d429   Stefan Wahren   hwmon: (pwm-fan) ...
18
  #include <linux/regulator/consumer.h>
d82d57767   Kamil Debski   hwmon: Add pwm-fa...
19
  #include <linux/sysfs.h>
b6bddec01   Lukasz Majewski   hwmon: (pwm-fan) ...
20
  #include <linux/thermal.h>
6b1ec4789   Stefan Wahren   hwmon: (pwm-fan) ...
21
  #include <linux/timer.h>
d82d57767   Kamil Debski   hwmon: Add pwm-fa...
22
23
24
25
26
27
  
  #define MAX_PWM 255
  
  struct pwm_fan_ctx {
  	struct mutex lock;
  	struct pwm_device *pwm;
b57e1d429   Stefan Wahren   hwmon: (pwm-fan) ...
28
  	struct regulator *reg_en;
6b1ec4789   Stefan Wahren   hwmon: (pwm-fan) ...
29
30
31
32
33
34
35
  
  	int irq;
  	atomic_t pulses;
  	unsigned int rpm;
  	u8 pulses_per_revolution;
  	ktime_t sample_start;
  	struct timer_list rpm_timer;
2e5219c77   Lukasz Majewski   hwmon: (pwm-fan) ...
36
37
38
39
  	unsigned int pwm_value;
  	unsigned int pwm_fan_state;
  	unsigned int pwm_fan_max_state;
  	unsigned int *pwm_fan_cooling_levels;
b6bddec01   Lukasz Majewski   hwmon: (pwm-fan) ...
40
  	struct thermal_cooling_device *cdev;
d82d57767   Kamil Debski   hwmon: Add pwm-fa...
41
  };
6b1ec4789   Stefan Wahren   hwmon: (pwm-fan) ...
42
43
44
45
46
47
48
49
50
51
52
53
54
  /* This handler assumes self resetting edge triggered interrupt. */
  static irqreturn_t pulse_handler(int irq, void *dev_id)
  {
  	struct pwm_fan_ctx *ctx = dev_id;
  
  	atomic_inc(&ctx->pulses);
  
  	return IRQ_HANDLED;
  }
  
  static void sample_timer(struct timer_list *t)
  {
  	struct pwm_fan_ctx *ctx = from_timer(ctx, t, rpm_timer);
fd8feec66   Paul Barker   hwmon: (pwm-fan) ...
55
  	unsigned int delta = ktime_ms_delta(ktime_get(), ctx->sample_start);
6b1ec4789   Stefan Wahren   hwmon: (pwm-fan) ...
56
  	int pulses;
6b1ec4789   Stefan Wahren   hwmon: (pwm-fan) ...
57

fd8feec66   Paul Barker   hwmon: (pwm-fan) ...
58
59
60
61
62
63
64
65
  	if (delta) {
  		pulses = atomic_read(&ctx->pulses);
  		atomic_sub(pulses, &ctx->pulses);
  		ctx->rpm = (unsigned int)(pulses * 1000 * 60) /
  			(ctx->pulses_per_revolution * delta);
  
  		ctx->sample_start = ktime_get();
  	}
6b1ec4789   Stefan Wahren   hwmon: (pwm-fan) ...
66

6b1ec4789   Stefan Wahren   hwmon: (pwm-fan) ...
67
68
  	mod_timer(&ctx->rpm_timer, jiffies + HZ);
  }
cb85ca332   Lukasz Majewski   hwmon: (pwm-fan) ...
69
  static int  __set_pwm(struct pwm_fan_ctx *ctx, unsigned long pwm)
d82d57767   Kamil Debski   hwmon: Add pwm-fa...
70
  {
677252a18   Bartlomiej Zolnierkiewicz   hwmon: (pwm-fan) ...
71
  	unsigned long period;
cb85ca332   Lukasz Majewski   hwmon: (pwm-fan) ...
72
  	int ret = 0;
677252a18   Bartlomiej Zolnierkiewicz   hwmon: (pwm-fan) ...
73
  	struct pwm_state state = { };
2289711c9   Boris Brezillon   hwmon: pwm-fan: U...
74

d82d57767   Kamil Debski   hwmon: Add pwm-fa...
75
  	mutex_lock(&ctx->lock);
d82d57767   Kamil Debski   hwmon: Add pwm-fa...
76
  	if (ctx->pwm_value == pwm)
cb85ca332   Lukasz Majewski   hwmon: (pwm-fan) ...
77
  		goto exit_set_pwm_err;
d82d57767   Kamil Debski   hwmon: Add pwm-fa...
78

677252a18   Bartlomiej Zolnierkiewicz   hwmon: (pwm-fan) ...
79
80
81
82
  	pwm_init_state(ctx->pwm, &state);
  	period = ctx->pwm->args.period;
  	state.duty_cycle = DIV_ROUND_UP(pwm * (period - 1), MAX_PWM);
  	state.enabled = pwm ? true : false;
f354169e0   Anand Moon   hwmon: (pwm-fan) ...
83

677252a18   Bartlomiej Zolnierkiewicz   hwmon: (pwm-fan) ...
84
85
86
  	ret = pwm_apply_state(ctx->pwm, &state);
  	if (!ret)
  		ctx->pwm_value = pwm;
d82d57767   Kamil Debski   hwmon: Add pwm-fa...
87
88
89
90
  exit_set_pwm_err:
  	mutex_unlock(&ctx->lock);
  	return ret;
  }
b6bddec01   Lukasz Majewski   hwmon: (pwm-fan) ...
91
92
93
94
95
96
97
98
99
100
  static void pwm_fan_update_state(struct pwm_fan_ctx *ctx, unsigned long pwm)
  {
  	int i;
  
  	for (i = 0; i < ctx->pwm_fan_max_state; ++i)
  		if (pwm < ctx->pwm_fan_cooling_levels[i + 1])
  			break;
  
  	ctx->pwm_fan_state = i;
  }
cb1d85341   Guenter Roeck   hwmon: (pwm-fan) ...
101
102
  static ssize_t pwm_store(struct device *dev, struct device_attribute *attr,
  			 const char *buf, size_t count)
cb85ca332   Lukasz Majewski   hwmon: (pwm-fan) ...
103
104
105
106
107
108
109
110
111
112
113
  {
  	struct pwm_fan_ctx *ctx = dev_get_drvdata(dev);
  	unsigned long pwm;
  	int ret;
  
  	if (kstrtoul(buf, 10, &pwm) || pwm > MAX_PWM)
  		return -EINVAL;
  
  	ret = __set_pwm(ctx, pwm);
  	if (ret)
  		return ret;
b6bddec01   Lukasz Majewski   hwmon: (pwm-fan) ...
114
  	pwm_fan_update_state(ctx, pwm);
cb85ca332   Lukasz Majewski   hwmon: (pwm-fan) ...
115
116
  	return count;
  }
cb1d85341   Guenter Roeck   hwmon: (pwm-fan) ...
117
118
  static ssize_t pwm_show(struct device *dev, struct device_attribute *attr,
  			char *buf)
d82d57767   Kamil Debski   hwmon: Add pwm-fa...
119
120
121
122
123
124
  {
  	struct pwm_fan_ctx *ctx = dev_get_drvdata(dev);
  
  	return sprintf(buf, "%u
  ", ctx->pwm_value);
  }
6b1ec4789   Stefan Wahren   hwmon: (pwm-fan) ...
125
126
127
128
129
130
131
132
  static ssize_t rpm_show(struct device *dev,
  			struct device_attribute *attr, char *buf)
  {
  	struct pwm_fan_ctx *ctx = dev_get_drvdata(dev);
  
  	return sprintf(buf, "%u
  ", ctx->rpm);
  }
d82d57767   Kamil Debski   hwmon: Add pwm-fa...
133

cb1d85341   Guenter Roeck   hwmon: (pwm-fan) ...
134
  static SENSOR_DEVICE_ATTR_RW(pwm1, pwm, 0);
6b1ec4789   Stefan Wahren   hwmon: (pwm-fan) ...
135
  static SENSOR_DEVICE_ATTR_RO(fan1_input, rpm, 0);
d82d57767   Kamil Debski   hwmon: Add pwm-fa...
136
137
138
  
  static struct attribute *pwm_fan_attrs[] = {
  	&sensor_dev_attr_pwm1.dev_attr.attr,
6b1ec4789   Stefan Wahren   hwmon: (pwm-fan) ...
139
  	&sensor_dev_attr_fan1_input.dev_attr.attr,
d82d57767   Kamil Debski   hwmon: Add pwm-fa...
140
141
  	NULL,
  };
6b1ec4789   Stefan Wahren   hwmon: (pwm-fan) ...
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
  static umode_t pwm_fan_attrs_visible(struct kobject *kobj, struct attribute *a,
  				     int n)
  {
  	struct device *dev = container_of(kobj, struct device, kobj);
  	struct pwm_fan_ctx *ctx = dev_get_drvdata(dev);
  
  	/* Hide fan_input in case no interrupt is available  */
  	if (n == 1 && ctx->irq <= 0)
  		return 0;
  
  	return a->mode;
  }
  
  static const struct attribute_group pwm_fan_group = {
  	.attrs = pwm_fan_attrs,
  	.is_visible = pwm_fan_attrs_visible,
  };
  
  static const struct attribute_group *pwm_fan_groups[] = {
  	&pwm_fan_group,
  	NULL,
  };
d82d57767   Kamil Debski   hwmon: Add pwm-fa...
164

b6bddec01   Lukasz Majewski   hwmon: (pwm-fan) ...
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
  /* thermal cooling device callbacks */
  static int pwm_fan_get_max_state(struct thermal_cooling_device *cdev,
  				 unsigned long *state)
  {
  	struct pwm_fan_ctx *ctx = cdev->devdata;
  
  	if (!ctx)
  		return -EINVAL;
  
  	*state = ctx->pwm_fan_max_state;
  
  	return 0;
  }
  
  static int pwm_fan_get_cur_state(struct thermal_cooling_device *cdev,
  				 unsigned long *state)
  {
  	struct pwm_fan_ctx *ctx = cdev->devdata;
  
  	if (!ctx)
  		return -EINVAL;
  
  	*state = ctx->pwm_fan_state;
  
  	return 0;
  }
  
  static int
  pwm_fan_set_cur_state(struct thermal_cooling_device *cdev, unsigned long state)
  {
  	struct pwm_fan_ctx *ctx = cdev->devdata;
  	int ret;
  
  	if (!ctx || (state > ctx->pwm_fan_max_state))
  		return -EINVAL;
  
  	if (state == ctx->pwm_fan_state)
  		return 0;
  
  	ret = __set_pwm(ctx, ctx->pwm_fan_cooling_levels[state]);
  	if (ret) {
  		dev_err(&cdev->device, "Cannot set pwm!
  ");
  		return ret;
  	}
  
  	ctx->pwm_fan_state = state;
  
  	return ret;
  }
  
  static const 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,
  };
de52b049d   Guenter Roeck   hwmon: (pwm-fan) ...
221
222
  static int pwm_fan_of_get_cooling_data(struct device *dev,
  				       struct pwm_fan_ctx *ctx)
2e5219c77   Lukasz Majewski   hwmon: (pwm-fan) ...
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
  {
  	struct device_node *np = dev->of_node;
  	int num, i, ret;
  
  	if (!of_find_property(np, "cooling-levels", NULL))
  		return 0;
  
  	ret = of_property_count_u32_elems(np, "cooling-levels");
  	if (ret <= 0) {
  		dev_err(dev, "Wrong data!
  ");
  		return ret ? : -EINVAL;
  	}
  
  	num = ret;
a86854d0c   Kees Cook   treewide: devm_kz...
238
  	ctx->pwm_fan_cooling_levels = devm_kcalloc(dev, num, sizeof(u32),
2e5219c77   Lukasz Majewski   hwmon: (pwm-fan) ...
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
  						   GFP_KERNEL);
  	if (!ctx->pwm_fan_cooling_levels)
  		return -ENOMEM;
  
  	ret = of_property_read_u32_array(np, "cooling-levels",
  					 ctx->pwm_fan_cooling_levels, num);
  	if (ret) {
  		dev_err(dev, "Property 'cooling-levels' cannot be read!
  ");
  		return ret;
  	}
  
  	for (i = 0; i < num; i++) {
  		if (ctx->pwm_fan_cooling_levels[i] > MAX_PWM) {
  			dev_err(dev, "PWM fan state[%d]:%d > %d
  ", i,
  				ctx->pwm_fan_cooling_levels[i], MAX_PWM);
  			return -EINVAL;
  		}
  	}
  
  	ctx->pwm_fan_max_state = num - 1;
  
  	return 0;
  }
37bcec5d9   Guenter Roeck   hwmon: (pwm-fan) ...
264
265
266
267
  static void pwm_fan_regulator_disable(void *data)
  {
  	regulator_disable(data);
  }
a455eda33   Linus Torvalds   Merge branch 'lin...
268
  static void pwm_fan_pwm_disable(void *__ctx)
37bcec5d9   Guenter Roeck   hwmon: (pwm-fan) ...
269
  {
a455eda33   Linus Torvalds   Merge branch 'lin...
270
271
272
  	struct pwm_fan_ctx *ctx = __ctx;
  	pwm_disable(ctx->pwm);
  	del_timer_sync(&ctx->rpm_timer);
37bcec5d9   Guenter Roeck   hwmon: (pwm-fan) ...
273
  }
d82d57767   Kamil Debski   hwmon: Add pwm-fa...
274
275
  static int pwm_fan_probe(struct platform_device *pdev)
  {
b6bddec01   Lukasz Majewski   hwmon: (pwm-fan) ...
276
  	struct thermal_cooling_device *cdev;
37bcec5d9   Guenter Roeck   hwmon: (pwm-fan) ...
277
  	struct device *dev = &pdev->dev;
d82d57767   Kamil Debski   hwmon: Add pwm-fa...
278
  	struct pwm_fan_ctx *ctx;
b6bddec01   Lukasz Majewski   hwmon: (pwm-fan) ...
279
  	struct device *hwmon;
d82d57767   Kamil Debski   hwmon: Add pwm-fa...
280
  	int ret;
677252a18   Bartlomiej Zolnierkiewicz   hwmon: (pwm-fan) ...
281
  	struct pwm_state state = { };
6b1ec4789   Stefan Wahren   hwmon: (pwm-fan) ...
282
  	u32 ppr = 2;
d82d57767   Kamil Debski   hwmon: Add pwm-fa...
283

37bcec5d9   Guenter Roeck   hwmon: (pwm-fan) ...
284
  	ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL);
d82d57767   Kamil Debski   hwmon: Add pwm-fa...
285
286
287
288
  	if (!ctx)
  		return -ENOMEM;
  
  	mutex_init(&ctx->lock);
37bcec5d9   Guenter Roeck   hwmon: (pwm-fan) ...
289
  	ctx->pwm = devm_of_pwm_get(dev, dev->of_node, NULL);
65b2aad06   Anson Huang   hwmon: (pwm-fan) ...
290
291
292
  	if (IS_ERR(ctx->pwm))
  		return dev_err_probe(dev, PTR_ERR(ctx->pwm), "Could not get PWM
  ");
d82d57767   Kamil Debski   hwmon: Add pwm-fa...
293

d82d57767   Kamil Debski   hwmon: Add pwm-fa...
294
  	platform_set_drvdata(pdev, ctx);
6e7e5c7fb   Thierry Reding   hwmon: pwm-fan: U...
295
  	ctx->irq = platform_get_irq_optional(pdev, 0);
6b1ec4789   Stefan Wahren   hwmon: (pwm-fan) ...
296
297
  	if (ctx->irq == -EPROBE_DEFER)
  		return ctx->irq;
37bcec5d9   Guenter Roeck   hwmon: (pwm-fan) ...
298
  	ctx->reg_en = devm_regulator_get_optional(dev, "fan");
b57e1d429   Stefan Wahren   hwmon: (pwm-fan) ...
299
300
301
302
303
304
305
306
  	if (IS_ERR(ctx->reg_en)) {
  		if (PTR_ERR(ctx->reg_en) != -ENODEV)
  			return PTR_ERR(ctx->reg_en);
  
  		ctx->reg_en = NULL;
  	} else {
  		ret = regulator_enable(ctx->reg_en);
  		if (ret) {
37bcec5d9   Guenter Roeck   hwmon: (pwm-fan) ...
307
308
  			dev_err(dev, "Failed to enable fan supply: %d
  ", ret);
b57e1d429   Stefan Wahren   hwmon: (pwm-fan) ...
309
310
  			return ret;
  		}
5696e4aaa   Guenter Roeck   hwmon: (pwm-fan) ...
311
312
313
314
  		ret = devm_add_action_or_reset(dev, pwm_fan_regulator_disable,
  					       ctx->reg_en);
  		if (ret)
  			return ret;
b57e1d429   Stefan Wahren   hwmon: (pwm-fan) ...
315
  	}
d82d57767   Kamil Debski   hwmon: Add pwm-fa...
316
  	ctx->pwm_value = MAX_PWM;
677252a18   Bartlomiej Zolnierkiewicz   hwmon: (pwm-fan) ...
317
  	pwm_init_state(ctx->pwm, &state);
3163d7c1f   Uwe Kleine-König   hwmon: (pwm-fan) ...
318
319
320
321
322
323
324
325
326
327
328
329
  	/*
  	 * __set_pwm assumes that MAX_PWM * (period - 1) fits into an unsigned
  	 * long. Check this here to prevent the fan running at a too low
  	 * frequency.
  	 */
  	if (state.period > ULONG_MAX / MAX_PWM + 1) {
  		dev_err(dev, "Configured period too big
  ");
  		return -EINVAL;
  	}
  
  	/* Set duty cycle to maximum allowed and enable PWM output */
677252a18   Bartlomiej Zolnierkiewicz   hwmon: (pwm-fan) ...
330
331
  	state.duty_cycle = ctx->pwm->args.period - 1;
  	state.enabled = true;
d82d57767   Kamil Debski   hwmon: Add pwm-fa...
332

677252a18   Bartlomiej Zolnierkiewicz   hwmon: (pwm-fan) ...
333
  	ret = pwm_apply_state(ctx->pwm, &state);
d82d57767   Kamil Debski   hwmon: Add pwm-fa...
334
  	if (ret) {
a455eda33   Linus Torvalds   Merge branch 'lin...
335
336
  		dev_err(dev, "Failed to configure PWM: %d
  ", ret);
37bcec5d9   Guenter Roeck   hwmon: (pwm-fan) ...
337
  		return ret;
d82d57767   Kamil Debski   hwmon: Add pwm-fa...
338
  	}
6b1ec4789   Stefan Wahren   hwmon: (pwm-fan) ...
339
  	timer_setup(&ctx->rpm_timer, sample_timer, 0);
5696e4aaa   Guenter Roeck   hwmon: (pwm-fan) ...
340
341
342
  	ret = devm_add_action_or_reset(dev, pwm_fan_pwm_disable, ctx);
  	if (ret)
  		return ret;
6b1ec4789   Stefan Wahren   hwmon: (pwm-fan) ...
343

a455eda33   Linus Torvalds   Merge branch 'lin...
344
  	of_property_read_u32(dev->of_node, "pulses-per-revolution", &ppr);
6b1ec4789   Stefan Wahren   hwmon: (pwm-fan) ...
345
346
  	ctx->pulses_per_revolution = ppr;
  	if (!ctx->pulses_per_revolution) {
a455eda33   Linus Torvalds   Merge branch 'lin...
347
348
349
  		dev_err(dev, "pulses-per-revolution can't be zero.
  ");
  		return -EINVAL;
6b1ec4789   Stefan Wahren   hwmon: (pwm-fan) ...
350
351
352
  	}
  
  	if (ctx->irq > 0) {
a455eda33   Linus Torvalds   Merge branch 'lin...
353
  		ret = devm_request_irq(dev, ctx->irq, pulse_handler, 0,
6b1ec4789   Stefan Wahren   hwmon: (pwm-fan) ...
354
355
  				       pdev->name, ctx);
  		if (ret) {
a455eda33   Linus Torvalds   Merge branch 'lin...
356
357
358
  			dev_err(dev, "Failed to request interrupt: %d
  ", ret);
  			return ret;
6b1ec4789   Stefan Wahren   hwmon: (pwm-fan) ...
359
360
361
362
  		}
  		ctx->sample_start = ktime_get();
  		mod_timer(&ctx->rpm_timer, jiffies + HZ);
  	}
37bcec5d9   Guenter Roeck   hwmon: (pwm-fan) ...
363
  	hwmon = devm_hwmon_device_register_with_groups(dev, "pwmfan",
d82d57767   Kamil Debski   hwmon: Add pwm-fa...
364
365
  						       ctx, pwm_fan_groups);
  	if (IS_ERR(hwmon)) {
37bcec5d9   Guenter Roeck   hwmon: (pwm-fan) ...
366
367
368
  		dev_err(dev, "Failed to register hwmon device
  ");
  		return PTR_ERR(hwmon);
d82d57767   Kamil Debski   hwmon: Add pwm-fa...
369
  	}
2e5219c77   Lukasz Majewski   hwmon: (pwm-fan) ...
370

37bcec5d9   Guenter Roeck   hwmon: (pwm-fan) ...
371
  	ret = pwm_fan_of_get_cooling_data(dev, ctx);
2e5219c77   Lukasz Majewski   hwmon: (pwm-fan) ...
372
373
  	if (ret)
  		return ret;
b6bddec01   Lukasz Majewski   hwmon: (pwm-fan) ...
374
375
  	ctx->pwm_fan_state = ctx->pwm_fan_max_state;
  	if (IS_ENABLED(CONFIG_THERMAL)) {
37bcec5d9   Guenter Roeck   hwmon: (pwm-fan) ...
376
377
  		cdev = devm_thermal_of_cooling_device_register(dev,
  			dev->of_node, "pwm-fan", ctx, &pwm_fan_cooling_ops);
b6bddec01   Lukasz Majewski   hwmon: (pwm-fan) ...
378
  		if (IS_ERR(cdev)) {
677252a18   Bartlomiej Zolnierkiewicz   hwmon: (pwm-fan) ...
379
  			ret = PTR_ERR(cdev);
37bcec5d9   Guenter Roeck   hwmon: (pwm-fan) ...
380
  			dev_err(dev,
841cf6767   Robin Murphy   hwmon: (pwm-fan) ...
381
382
383
  				"Failed to register pwm-fan as cooling device: %d
  ",
  				ret);
a455eda33   Linus Torvalds   Merge branch 'lin...
384
  			return ret;
b6bddec01   Lukasz Majewski   hwmon: (pwm-fan) ...
385
386
387
388
  		}
  		ctx->cdev = cdev;
  		thermal_cdev_update(cdev);
  	}
d82d57767   Kamil Debski   hwmon: Add pwm-fa...
389
  	return 0;
d82d57767   Kamil Debski   hwmon: Add pwm-fa...
390
  }
7992db7cb   Akinobu Mita   hwmon: (pwm-fan) ...
391
  static int pwm_fan_disable(struct device *dev)
d82d57767   Kamil Debski   hwmon: Add pwm-fa...
392
393
  {
  	struct pwm_fan_ctx *ctx = dev_get_drvdata(dev);
95dcd64bc   Thierry Reding   hwmon: (pwm-fan) ...
394
395
396
397
398
399
400
401
402
  	struct pwm_args args;
  	int ret;
  
  	pwm_get_args(ctx->pwm, &args);
  
  	if (ctx->pwm_value) {
  		ret = pwm_config(ctx->pwm, 0, args.period);
  		if (ret < 0)
  			return ret;
d82d57767   Kamil Debski   hwmon: Add pwm-fa...
403

d82d57767   Kamil Debski   hwmon: Add pwm-fa...
404
  		pwm_disable(ctx->pwm);
95dcd64bc   Thierry Reding   hwmon: (pwm-fan) ...
405
  	}
b57e1d429   Stefan Wahren   hwmon: (pwm-fan) ...
406
407
408
409
410
411
412
413
  	if (ctx->reg_en) {
  		ret = regulator_disable(ctx->reg_en);
  		if (ret) {
  			dev_err(dev, "Failed to disable fan supply: %d
  ", ret);
  			return ret;
  		}
  	}
d82d57767   Kamil Debski   hwmon: Add pwm-fa...
414
415
  	return 0;
  }
7992db7cb   Akinobu Mita   hwmon: (pwm-fan) ...
416
417
418
419
420
421
422
423
424
425
  static void pwm_fan_shutdown(struct platform_device *pdev)
  {
  	pwm_fan_disable(&pdev->dev);
  }
  
  #ifdef CONFIG_PM_SLEEP
  static int pwm_fan_suspend(struct device *dev)
  {
  	return pwm_fan_disable(dev);
  }
d82d57767   Kamil Debski   hwmon: Add pwm-fa...
426
427
428
  static int pwm_fan_resume(struct device *dev)
  {
  	struct pwm_fan_ctx *ctx = dev_get_drvdata(dev);
2289711c9   Boris Brezillon   hwmon: pwm-fan: U...
429
  	struct pwm_args pargs;
48b9d5b4f   Kamil Debski   hwmon: (pwm-fan) ...
430
431
  	unsigned long duty;
  	int ret;
d82d57767   Kamil Debski   hwmon: Add pwm-fa...
432

b57e1d429   Stefan Wahren   hwmon: (pwm-fan) ...
433
434
435
436
437
438
439
440
  	if (ctx->reg_en) {
  		ret = regulator_enable(ctx->reg_en);
  		if (ret) {
  			dev_err(dev, "Failed to enable fan supply: %d
  ", ret);
  			return ret;
  		}
  	}
48b9d5b4f   Kamil Debski   hwmon: (pwm-fan) ...
441
442
  	if (ctx->pwm_value == 0)
  		return 0;
2289711c9   Boris Brezillon   hwmon: pwm-fan: U...
443
  	pwm_get_args(ctx->pwm, &pargs);
f3e4b1414   Guru Das Srinagesh   hwmon: pwm-fan: U...
444
  	duty = DIV_ROUND_UP_ULL(ctx->pwm_value * (pargs.period - 1), MAX_PWM);
2289711c9   Boris Brezillon   hwmon: pwm-fan: U...
445
  	ret = pwm_config(ctx->pwm, duty, pargs.period);
48b9d5b4f   Kamil Debski   hwmon: (pwm-fan) ...
446
447
448
  	if (ret)
  		return ret;
  	return pwm_enable(ctx->pwm);
d82d57767   Kamil Debski   hwmon: Add pwm-fa...
449
450
451
452
  }
  #endif
  
  static SIMPLE_DEV_PM_OPS(pwm_fan_pm, pwm_fan_suspend, pwm_fan_resume);
d720acace   Fabian Frederick   hwmon: (pwm-fan, ...
453
  static const struct of_device_id of_pwm_fan_match[] = {
d82d57767   Kamil Debski   hwmon: Add pwm-fa...
454
455
456
  	{ .compatible = "pwm-fan", },
  	{},
  };
f491e70cc   Luis de Bethencourt   hwmon: (pwm-fan) ...
457
  MODULE_DEVICE_TABLE(of, of_pwm_fan_match);
d82d57767   Kamil Debski   hwmon: Add pwm-fa...
458
459
460
  
  static struct platform_driver pwm_fan_driver = {
  	.probe		= pwm_fan_probe,
7992db7cb   Akinobu Mita   hwmon: (pwm-fan) ...
461
  	.shutdown	= pwm_fan_shutdown,
d82d57767   Kamil Debski   hwmon: Add pwm-fa...
462
463
464
465
466
467
468
469
470
471
472
473
474
  	.driver	= {
  		.name		= "pwm-fan",
  		.pm		= &pwm_fan_pm,
  		.of_match_table	= of_pwm_fan_match,
  	},
  };
  
  module_platform_driver(pwm_fan_driver);
  
  MODULE_AUTHOR("Kamil Debski <k.debski@samsung.com>");
  MODULE_ALIAS("platform:pwm-fan");
  MODULE_DESCRIPTION("PWM FAN driver");
  MODULE_LICENSE("GPL");