Blame view

drivers/pwm/sysfs.c 9.37 KB
76abbdde2   H Hartley Sweeten   pwm: Add sysfs in...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
  /*
   * A simple sysfs interface for the generic PWM framework
   *
   * Copyright (C) 2013 H Hartley Sweeten <hsweeten@visionengravers.com>
   *
   * Based on previous work by Lars Poeschel <poeschel@lemonage.de>
   *
   * This program is free software; you can redistribute it and/or modify
   * it under the terms of the GNU General Public License as published by
   * the Free Software Foundation; either version 2, or (at your option)
   * any later version.
   *
   * This program is distributed in the hope that it will be useful,
   * but WITHOUT ANY WARRANTY; without even the implied warranty of
   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   * GNU General Public License for more details.
   */
  
  #include <linux/device.h>
  #include <linux/mutex.h>
  #include <linux/err.h>
  #include <linux/slab.h>
  #include <linux/kdev_t.h>
  #include <linux/pwm.h>
  
  struct pwm_export {
  	struct device child;
  	struct pwm_device *pwm;
459a25afe   Boris BREZILLON   pwm: Get rid of p...
29
  	struct mutex lock;
76abbdde2   H Hartley Sweeten   pwm: Add sysfs in...
30
31
32
33
34
35
36
37
38
39
40
41
42
  };
  
  static struct pwm_export *child_to_pwm_export(struct device *child)
  {
  	return container_of(child, struct pwm_export, child);
  }
  
  static struct pwm_device *child_to_pwm_device(struct device *child)
  {
  	struct pwm_export *export = child_to_pwm_export(child);
  
  	return export->pwm;
  }
65cdc6914   Olliver Schinagl   pwm: sysfs: Make ...
43
44
45
  static ssize_t period_show(struct device *child,
  			   struct device_attribute *attr,
  			   char *buf)
76abbdde2   H Hartley Sweeten   pwm: Add sysfs in...
46
47
  {
  	const struct pwm_device *pwm = child_to_pwm_device(child);
39100ceea   Boris Brezillon   pwm: Switch to th...
48
  	struct pwm_state state;
76abbdde2   H Hartley Sweeten   pwm: Add sysfs in...
49

39100ceea   Boris Brezillon   pwm: Switch to th...
50
51
52
53
  	pwm_get_state(pwm, &state);
  
  	return sprintf(buf, "%u
  ", state.period);
76abbdde2   H Hartley Sweeten   pwm: Add sysfs in...
54
  }
65cdc6914   Olliver Schinagl   pwm: sysfs: Make ...
55
56
57
  static ssize_t period_store(struct device *child,
  			    struct device_attribute *attr,
  			    const char *buf, size_t size)
76abbdde2   H Hartley Sweeten   pwm: Add sysfs in...
58
  {
459a25afe   Boris BREZILLON   pwm: Get rid of p...
59
60
  	struct pwm_export *export = child_to_pwm_export(child);
  	struct pwm_device *pwm = export->pwm;
39100ceea   Boris Brezillon   pwm: Switch to th...
61
  	struct pwm_state state;
76abbdde2   H Hartley Sweeten   pwm: Add sysfs in...
62
63
64
65
66
67
  	unsigned int val;
  	int ret;
  
  	ret = kstrtouint(buf, 0, &val);
  	if (ret)
  		return ret;
459a25afe   Boris BREZILLON   pwm: Get rid of p...
68
  	mutex_lock(&export->lock);
39100ceea   Boris Brezillon   pwm: Switch to th...
69
70
71
  	pwm_get_state(pwm, &state);
  	state.period = val;
  	ret = pwm_apply_state(pwm, &state);
459a25afe   Boris BREZILLON   pwm: Get rid of p...
72
  	mutex_unlock(&export->lock);
76abbdde2   H Hartley Sweeten   pwm: Add sysfs in...
73
74
75
  
  	return ret ? : size;
  }
65cdc6914   Olliver Schinagl   pwm: sysfs: Make ...
76
77
78
  static ssize_t duty_cycle_show(struct device *child,
  			       struct device_attribute *attr,
  			       char *buf)
76abbdde2   H Hartley Sweeten   pwm: Add sysfs in...
79
80
  {
  	const struct pwm_device *pwm = child_to_pwm_device(child);
39100ceea   Boris Brezillon   pwm: Switch to th...
81
82
83
  	struct pwm_state state;
  
  	pwm_get_state(pwm, &state);
76abbdde2   H Hartley Sweeten   pwm: Add sysfs in...
84

39100ceea   Boris Brezillon   pwm: Switch to th...
85
86
  	return sprintf(buf, "%u
  ", state.duty_cycle);
76abbdde2   H Hartley Sweeten   pwm: Add sysfs in...
87
  }
65cdc6914   Olliver Schinagl   pwm: sysfs: Make ...
88
89
90
  static ssize_t duty_cycle_store(struct device *child,
  				struct device_attribute *attr,
  				const char *buf, size_t size)
76abbdde2   H Hartley Sweeten   pwm: Add sysfs in...
91
  {
459a25afe   Boris BREZILLON   pwm: Get rid of p...
92
93
  	struct pwm_export *export = child_to_pwm_export(child);
  	struct pwm_device *pwm = export->pwm;
39100ceea   Boris Brezillon   pwm: Switch to th...
94
  	struct pwm_state state;
76abbdde2   H Hartley Sweeten   pwm: Add sysfs in...
95
96
97
98
99
100
  	unsigned int val;
  	int ret;
  
  	ret = kstrtouint(buf, 0, &val);
  	if (ret)
  		return ret;
459a25afe   Boris BREZILLON   pwm: Get rid of p...
101
  	mutex_lock(&export->lock);
39100ceea   Boris Brezillon   pwm: Switch to th...
102
103
104
  	pwm_get_state(pwm, &state);
  	state.duty_cycle = val;
  	ret = pwm_apply_state(pwm, &state);
459a25afe   Boris BREZILLON   pwm: Get rid of p...
105
  	mutex_unlock(&export->lock);
76abbdde2   H Hartley Sweeten   pwm: Add sysfs in...
106
107
108
  
  	return ret ? : size;
  }
65cdc6914   Olliver Schinagl   pwm: sysfs: Make ...
109
110
111
  static ssize_t enable_show(struct device *child,
  			   struct device_attribute *attr,
  			   char *buf)
76abbdde2   H Hartley Sweeten   pwm: Add sysfs in...
112
113
  {
  	const struct pwm_device *pwm = child_to_pwm_device(child);
39100ceea   Boris Brezillon   pwm: Switch to th...
114
  	struct pwm_state state;
76abbdde2   H Hartley Sweeten   pwm: Add sysfs in...
115

39100ceea   Boris Brezillon   pwm: Switch to th...
116
117
118
119
  	pwm_get_state(pwm, &state);
  
  	return sprintf(buf, "%d
  ", state.enabled);
76abbdde2   H Hartley Sweeten   pwm: Add sysfs in...
120
  }
65cdc6914   Olliver Schinagl   pwm: sysfs: Make ...
121
122
123
  static ssize_t enable_store(struct device *child,
  			    struct device_attribute *attr,
  			    const char *buf, size_t size)
76abbdde2   H Hartley Sweeten   pwm: Add sysfs in...
124
  {
459a25afe   Boris BREZILLON   pwm: Get rid of p...
125
126
  	struct pwm_export *export = child_to_pwm_export(child);
  	struct pwm_device *pwm = export->pwm;
39100ceea   Boris Brezillon   pwm: Switch to th...
127
  	struct pwm_state state;
76abbdde2   H Hartley Sweeten   pwm: Add sysfs in...
128
129
130
131
132
  	int val, ret;
  
  	ret = kstrtoint(buf, 0, &val);
  	if (ret)
  		return ret;
459a25afe   Boris BREZILLON   pwm: Get rid of p...
133
  	mutex_lock(&export->lock);
39100ceea   Boris Brezillon   pwm: Switch to th...
134
  	pwm_get_state(pwm, &state);
76abbdde2   H Hartley Sweeten   pwm: Add sysfs in...
135
136
  	switch (val) {
  	case 0:
39100ceea   Boris Brezillon   pwm: Switch to th...
137
  		state.enabled = false;
76abbdde2   H Hartley Sweeten   pwm: Add sysfs in...
138
139
  		break;
  	case 1:
39100ceea   Boris Brezillon   pwm: Switch to th...
140
  		state.enabled = true;
76abbdde2   H Hartley Sweeten   pwm: Add sysfs in...
141
142
143
  		break;
  	default:
  		ret = -EINVAL;
39100ceea   Boris Brezillon   pwm: Switch to th...
144
  		goto unlock;
76abbdde2   H Hartley Sweeten   pwm: Add sysfs in...
145
  	}
fe5aa34d6   Ryo Kodama   pwm: sysfs: Get r...
146
  	ret = pwm_apply_state(pwm, &state);
459a25afe   Boris BREZILLON   pwm: Get rid of p...
147

39100ceea   Boris Brezillon   pwm: Switch to th...
148
149
  unlock:
  	mutex_unlock(&export->lock);
76abbdde2   H Hartley Sweeten   pwm: Add sysfs in...
150
151
  	return ret ? : size;
  }
65cdc6914   Olliver Schinagl   pwm: sysfs: Make ...
152
153
154
  static ssize_t polarity_show(struct device *child,
  			     struct device_attribute *attr,
  			     char *buf)
76abbdde2   H Hartley Sweeten   pwm: Add sysfs in...
155
156
  {
  	const struct pwm_device *pwm = child_to_pwm_device(child);
5a063d87e   Thierry Reding   pwm: sysfs: Prope...
157
  	const char *polarity = "unknown";
39100ceea   Boris Brezillon   pwm: Switch to th...
158
159
160
  	struct pwm_state state;
  
  	pwm_get_state(pwm, &state);
76abbdde2   H Hartley Sweeten   pwm: Add sysfs in...
161

39100ceea   Boris Brezillon   pwm: Switch to th...
162
  	switch (state.polarity) {
5a063d87e   Thierry Reding   pwm: sysfs: Prope...
163
164
165
166
167
168
169
170
171
172
173
  	case PWM_POLARITY_NORMAL:
  		polarity = "normal";
  		break;
  
  	case PWM_POLARITY_INVERSED:
  		polarity = "inversed";
  		break;
  	}
  
  	return sprintf(buf, "%s
  ", polarity);
76abbdde2   H Hartley Sweeten   pwm: Add sysfs in...
174
  }
65cdc6914   Olliver Schinagl   pwm: sysfs: Make ...
175
176
177
  static ssize_t polarity_store(struct device *child,
  			      struct device_attribute *attr,
  			      const char *buf, size_t size)
76abbdde2   H Hartley Sweeten   pwm: Add sysfs in...
178
  {
459a25afe   Boris BREZILLON   pwm: Get rid of p...
179
180
  	struct pwm_export *export = child_to_pwm_export(child);
  	struct pwm_device *pwm = export->pwm;
76abbdde2   H Hartley Sweeten   pwm: Add sysfs in...
181
  	enum pwm_polarity polarity;
39100ceea   Boris Brezillon   pwm: Switch to th...
182
  	struct pwm_state state;
76abbdde2   H Hartley Sweeten   pwm: Add sysfs in...
183
184
185
186
187
188
189
190
  	int ret;
  
  	if (sysfs_streq(buf, "normal"))
  		polarity = PWM_POLARITY_NORMAL;
  	else if (sysfs_streq(buf, "inversed"))
  		polarity = PWM_POLARITY_INVERSED;
  	else
  		return -EINVAL;
459a25afe   Boris BREZILLON   pwm: Get rid of p...
191
  	mutex_lock(&export->lock);
39100ceea   Boris Brezillon   pwm: Switch to th...
192
193
194
  	pwm_get_state(pwm, &state);
  	state.polarity = polarity;
  	ret = pwm_apply_state(pwm, &state);
459a25afe   Boris BREZILLON   pwm: Get rid of p...
195
  	mutex_unlock(&export->lock);
76abbdde2   H Hartley Sweeten   pwm: Add sysfs in...
196
197
198
  
  	return ret ? : size;
  }
1a366fe91   Lee Jones   pwm: sysfs: Add P...
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
  static ssize_t capture_show(struct device *child,
  			    struct device_attribute *attr,
  			    char *buf)
  {
  	struct pwm_device *pwm = child_to_pwm_device(child);
  	struct pwm_capture result;
  	int ret;
  
  	ret = pwm_capture(pwm, &result, jiffies_to_msecs(HZ));
  	if (ret)
  		return ret;
  
  	return sprintf(buf, "%u %u
  ", result.period, result.duty_cycle);
  }
65cdc6914   Olliver Schinagl   pwm: sysfs: Make ...
214
215
216
217
  static DEVICE_ATTR_RW(period);
  static DEVICE_ATTR_RW(duty_cycle);
  static DEVICE_ATTR_RW(enable);
  static DEVICE_ATTR_RW(polarity);
1a366fe91   Lee Jones   pwm: sysfs: Add P...
218
  static DEVICE_ATTR_RO(capture);
76abbdde2   H Hartley Sweeten   pwm: Add sysfs in...
219
220
221
222
223
224
  
  static struct attribute *pwm_attrs[] = {
  	&dev_attr_period.attr,
  	&dev_attr_duty_cycle.attr,
  	&dev_attr_enable.attr,
  	&dev_attr_polarity.attr,
1a366fe91   Lee Jones   pwm: sysfs: Add P...
225
  	&dev_attr_capture.attr,
76abbdde2   H Hartley Sweeten   pwm: Add sysfs in...
226
227
  	NULL
  };
6ca142ad0   Axel Lin   pwm: sysfs: Conve...
228
  ATTRIBUTE_GROUPS(pwm);
76abbdde2   H Hartley Sweeten   pwm: Add sysfs in...
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
  
  static void pwm_export_release(struct device *child)
  {
  	struct pwm_export *export = child_to_pwm_export(child);
  
  	kfree(export);
  }
  
  static int pwm_export_child(struct device *parent, struct pwm_device *pwm)
  {
  	struct pwm_export *export;
  	int ret;
  
  	if (test_and_set_bit(PWMF_EXPORTED, &pwm->flags))
  		return -EBUSY;
  
  	export = kzalloc(sizeof(*export), GFP_KERNEL);
  	if (!export) {
  		clear_bit(PWMF_EXPORTED, &pwm->flags);
  		return -ENOMEM;
  	}
  
  	export->pwm = pwm;
459a25afe   Boris BREZILLON   pwm: Get rid of p...
252
  	mutex_init(&export->lock);
76abbdde2   H Hartley Sweeten   pwm: Add sysfs in...
253
254
255
256
  
  	export->child.release = pwm_export_release;
  	export->child.parent = parent;
  	export->child.devt = MKDEV(0, 0);
6ca142ad0   Axel Lin   pwm: sysfs: Conve...
257
  	export->child.groups = pwm_groups;
76abbdde2   H Hartley Sweeten   pwm: Add sysfs in...
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
  	dev_set_name(&export->child, "pwm%u", pwm->hwpwm);
  
  	ret = device_register(&export->child);
  	if (ret) {
  		clear_bit(PWMF_EXPORTED, &pwm->flags);
  		kfree(export);
  		return ret;
  	}
  
  	return 0;
  }
  
  static int pwm_unexport_match(struct device *child, void *data)
  {
  	return child_to_pwm_device(child) == data;
  }
  
  static int pwm_unexport_child(struct device *parent, struct pwm_device *pwm)
  {
  	struct device *child;
  
  	if (!test_and_clear_bit(PWMF_EXPORTED, &pwm->flags))
  		return -ENODEV;
  
  	child = device_find_child(parent, pwm, pwm_unexport_match);
  	if (!child)
  		return -ENODEV;
  
  	/* for device_find_child() */
  	put_device(child);
  	device_unregister(child);
  	pwm_put(pwm);
  
  	return 0;
  }
65cdc6914   Olliver Schinagl   pwm: sysfs: Make ...
293
294
295
  static ssize_t export_store(struct device *parent,
  			    struct device_attribute *attr,
  			    const char *buf, size_t len)
76abbdde2   H Hartley Sweeten   pwm: Add sysfs in...
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
  {
  	struct pwm_chip *chip = dev_get_drvdata(parent);
  	struct pwm_device *pwm;
  	unsigned int hwpwm;
  	int ret;
  
  	ret = kstrtouint(buf, 0, &hwpwm);
  	if (ret < 0)
  		return ret;
  
  	if (hwpwm >= chip->npwm)
  		return -ENODEV;
  
  	pwm = pwm_request_from_chip(chip, hwpwm, "sysfs");
  	if (IS_ERR(pwm))
  		return PTR_ERR(pwm);
  
  	ret = pwm_export_child(parent, pwm);
  	if (ret < 0)
  		pwm_put(pwm);
  
  	return ret ? : len;
  }
65cdc6914   Olliver Schinagl   pwm: sysfs: Make ...
319
  static DEVICE_ATTR_WO(export);
76abbdde2   H Hartley Sweeten   pwm: Add sysfs in...
320

65cdc6914   Olliver Schinagl   pwm: sysfs: Make ...
321
322
323
  static ssize_t unexport_store(struct device *parent,
  			      struct device_attribute *attr,
  			      const char *buf, size_t len)
76abbdde2   H Hartley Sweeten   pwm: Add sysfs in...
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
  {
  	struct pwm_chip *chip = dev_get_drvdata(parent);
  	unsigned int hwpwm;
  	int ret;
  
  	ret = kstrtouint(buf, 0, &hwpwm);
  	if (ret < 0)
  		return ret;
  
  	if (hwpwm >= chip->npwm)
  		return -ENODEV;
  
  	ret = pwm_unexport_child(parent, &chip->pwms[hwpwm]);
  
  	return ret ? : len;
  }
65cdc6914   Olliver Schinagl   pwm: sysfs: Make ...
340
  static DEVICE_ATTR_WO(unexport);
76abbdde2   H Hartley Sweeten   pwm: Add sysfs in...
341

9da017596   Greg Kroah-Hartman   pwm: convert clas...
342
343
  static ssize_t npwm_show(struct device *parent, struct device_attribute *attr,
  			 char *buf)
76abbdde2   H Hartley Sweeten   pwm: Add sysfs in...
344
345
346
347
348
349
  {
  	const struct pwm_chip *chip = dev_get_drvdata(parent);
  
  	return sprintf(buf, "%u
  ", chip->npwm);
  }
9da017596   Greg Kroah-Hartman   pwm: convert clas...
350
  static DEVICE_ATTR_RO(npwm);
76abbdde2   H Hartley Sweeten   pwm: Add sysfs in...
351

9da017596   Greg Kroah-Hartman   pwm: convert clas...
352
353
354
355
356
  static struct attribute *pwm_chip_attrs[] = {
  	&dev_attr_export.attr,
  	&dev_attr_unexport.attr,
  	&dev_attr_npwm.attr,
  	NULL,
76abbdde2   H Hartley Sweeten   pwm: Add sysfs in...
357
  };
9da017596   Greg Kroah-Hartman   pwm: convert clas...
358
  ATTRIBUTE_GROUPS(pwm_chip);
76abbdde2   H Hartley Sweeten   pwm: Add sysfs in...
359
360
  
  static struct class pwm_class = {
412820dd5   Thierry Reding   pwm: sysfs: Remov...
361
362
363
  	.name = "pwm",
  	.owner = THIS_MODULE,
  	.dev_groups = pwm_chip_groups,
76abbdde2   H Hartley Sweeten   pwm: Add sysfs in...
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
  };
  
  static int pwmchip_sysfs_match(struct device *parent, const void *data)
  {
  	return dev_get_drvdata(parent) == data;
  }
  
  void pwmchip_sysfs_export(struct pwm_chip *chip)
  {
  	struct device *parent;
  
  	/*
  	 * If device_create() fails the pwm_chip is still usable by
  	 * the kernel its just not exported.
  	 */
  	parent = device_create(&pwm_class, chip->dev, MKDEV(0, 0), chip,
  			       "pwmchip%d", chip->base);
  	if (IS_ERR(parent)) {
  		dev_warn(chip->dev,
  			 "device_create failed for pwm_chip sysfs export
  ");
  	}
  }
  
  void pwmchip_sysfs_unexport(struct pwm_chip *chip)
  {
  	struct device *parent;
  
  	parent = class_find_device(&pwm_class, NULL, chip,
  				   pwmchip_sysfs_match);
  	if (parent) {
  		/* for class_find_device() */
  		put_device(parent);
  		device_unregister(parent);
  	}
  }
0733424c9   David Hsu   pwm: Unexport chi...
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
  void pwmchip_sysfs_unexport_children(struct pwm_chip *chip)
  {
  	struct device *parent;
  	unsigned int i;
  
  	parent = class_find_device(&pwm_class, NULL, chip,
  				   pwmchip_sysfs_match);
  	if (!parent)
  		return;
  
  	for (i = 0; i < chip->npwm; i++) {
  		struct pwm_device *pwm = &chip->pwms[i];
  
  		if (test_bit(PWMF_EXPORTED, &pwm->flags))
  			pwm_unexport_child(parent, pwm);
  	}
0e1614ac8   Johan Hovold   pwm: Fix device r...
416
417
  
  	put_device(parent);
0733424c9   David Hsu   pwm: Unexport chi...
418
  }
76abbdde2   H Hartley Sweeten   pwm: Add sysfs in...
419
420
421
422
423
  static int __init pwm_sysfs_init(void)
  {
  	return class_register(&pwm_class);
  }
  subsys_initcall(pwm_sysfs_init);