[go: nahoru, domu]

176abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten/*
276abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten * A simple sysfs interface for the generic PWM framework
376abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten *
476abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten * Copyright (C) 2013 H Hartley Sweeten <hsweeten@visionengravers.com>
576abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten *
676abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten * Based on previous work by Lars Poeschel <poeschel@lemonage.de>
776abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten *
876abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten * This program is free software; you can redistribute it and/or modify
976abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten * it under the terms of the GNU General Public License as published by
1076abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten * the Free Software Foundation; either version 2, or (at your option)
1176abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten * any later version.
1276abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten *
1376abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten * This program is distributed in the hope that it will be useful,
1476abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten * but WITHOUT ANY WARRANTY; without even the implied warranty of
1576abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
1676abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten * GNU General Public License for more details.
1776abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten */
1876abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten
1976abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten#include <linux/device.h>
2076abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten#include <linux/mutex.h>
2176abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten#include <linux/err.h>
2276abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten#include <linux/slab.h>
2376abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten#include <linux/kdev_t.h>
2476abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten#include <linux/pwm.h>
2576abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten
2676abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweetenstruct pwm_export {
2776abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten	struct device child;
2876abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten	struct pwm_device *pwm;
2976abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten};
3076abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten
3176abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweetenstatic struct pwm_export *child_to_pwm_export(struct device *child)
3276abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten{
3376abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten	return container_of(child, struct pwm_export, child);
3476abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten}
3576abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten
3676abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweetenstatic struct pwm_device *child_to_pwm_device(struct device *child)
3776abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten{
3876abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten	struct pwm_export *export = child_to_pwm_export(child);
3976abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten
4076abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten	return export->pwm;
4176abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten}
4276abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten
4376abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweetenstatic ssize_t pwm_period_show(struct device *child,
4476abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten			       struct device_attribute *attr,
4576abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten			       char *buf)
4676abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten{
4776abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten	const struct pwm_device *pwm = child_to_pwm_device(child);
4876abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten
4976abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten	return sprintf(buf, "%u\n", pwm->period);
5076abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten}
5176abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten
5276abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweetenstatic ssize_t pwm_period_store(struct device *child,
5376abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten				struct device_attribute *attr,
5476abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten				const char *buf, size_t size)
5576abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten{
5676abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten	struct pwm_device *pwm = child_to_pwm_device(child);
5776abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten	unsigned int val;
5876abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten	int ret;
5976abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten
6076abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten	ret = kstrtouint(buf, 0, &val);
6176abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten	if (ret)
6276abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten		return ret;
6376abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten
6476abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten	ret = pwm_config(pwm, pwm->duty_cycle, val);
6576abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten
6676abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten	return ret ? : size;
6776abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten}
6876abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten
6976abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweetenstatic ssize_t pwm_duty_cycle_show(struct device *child,
7076abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten				   struct device_attribute *attr,
7176abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten				   char *buf)
7276abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten{
7376abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten	const struct pwm_device *pwm = child_to_pwm_device(child);
7476abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten
7576abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten	return sprintf(buf, "%u\n", pwm->duty_cycle);
7676abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten}
7776abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten
7876abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweetenstatic ssize_t pwm_duty_cycle_store(struct device *child,
7976abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten				    struct device_attribute *attr,
8076abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten				    const char *buf, size_t size)
8176abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten{
8276abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten	struct pwm_device *pwm = child_to_pwm_device(child);
8376abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten	unsigned int val;
8476abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten	int ret;
8576abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten
8676abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten	ret = kstrtouint(buf, 0, &val);
8776abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten	if (ret)
8876abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten		return ret;
8976abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten
9076abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten	ret = pwm_config(pwm, val, pwm->period);
9176abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten
9276abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten	return ret ? : size;
9376abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten}
9476abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten
9576abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweetenstatic ssize_t pwm_enable_show(struct device *child,
9676abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten			       struct device_attribute *attr,
9776abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten			       char *buf)
9876abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten{
9976abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten	const struct pwm_device *pwm = child_to_pwm_device(child);
10076abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten	int enabled = test_bit(PWMF_ENABLED, &pwm->flags);
10176abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten
10276abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten	return sprintf(buf, "%d\n", enabled);
10376abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten}
10476abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten
10576abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweetenstatic ssize_t pwm_enable_store(struct device *child,
10676abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten				struct device_attribute *attr,
10776abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten				const char *buf, size_t size)
10876abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten{
10976abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten	struct pwm_device *pwm = child_to_pwm_device(child);
11076abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten	int val, ret;
11176abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten
11276abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten	ret = kstrtoint(buf, 0, &val);
11376abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten	if (ret)
11476abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten		return ret;
11576abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten
11676abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten	switch (val) {
11776abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten	case 0:
11876abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten		pwm_disable(pwm);
11976abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten		break;
12076abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten	case 1:
12176abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten		ret = pwm_enable(pwm);
12276abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten		break;
12376abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten	default:
12476abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten		ret = -EINVAL;
12576abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten		break;
12676abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten	}
12776abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten
12876abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten	return ret ? : size;
12976abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten}
13076abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten
13176abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweetenstatic ssize_t pwm_polarity_show(struct device *child,
13276abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten				 struct device_attribute *attr,
13376abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten				 char *buf)
13476abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten{
13576abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten	const struct pwm_device *pwm = child_to_pwm_device(child);
13676abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten
13776abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten	return sprintf(buf, "%s\n", pwm->polarity ? "inversed" : "normal");
13876abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten}
13976abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten
14076abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweetenstatic ssize_t pwm_polarity_store(struct device *child,
14176abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten				  struct device_attribute *attr,
14276abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten				  const char *buf, size_t size)
14376abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten{
14476abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten	struct pwm_device *pwm = child_to_pwm_device(child);
14576abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten	enum pwm_polarity polarity;
14676abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten	int ret;
14776abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten
14876abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten	if (sysfs_streq(buf, "normal"))
14976abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten		polarity = PWM_POLARITY_NORMAL;
15076abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten	else if (sysfs_streq(buf, "inversed"))
15176abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten		polarity = PWM_POLARITY_INVERSED;
15276abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten	else
15376abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten		return -EINVAL;
15476abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten
15576abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten	ret = pwm_set_polarity(pwm, polarity);
15676abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten
15776abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten	return ret ? : size;
15876abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten}
15976abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten
16076abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweetenstatic DEVICE_ATTR(period, 0644, pwm_period_show, pwm_period_store);
16176abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweetenstatic DEVICE_ATTR(duty_cycle, 0644, pwm_duty_cycle_show, pwm_duty_cycle_store);
16276abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweetenstatic DEVICE_ATTR(enable, 0644, pwm_enable_show, pwm_enable_store);
16376abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweetenstatic DEVICE_ATTR(polarity, 0644, pwm_polarity_show, pwm_polarity_store);
16476abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten
16576abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweetenstatic struct attribute *pwm_attrs[] = {
16676abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten	&dev_attr_period.attr,
16776abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten	&dev_attr_duty_cycle.attr,
16876abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten	&dev_attr_enable.attr,
16976abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten	&dev_attr_polarity.attr,
17076abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten	NULL
17176abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten};
1726ca142ad0d1bdf8dd116351b7b9a2bc5670d5271Axel LinATTRIBUTE_GROUPS(pwm);
17376abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten
17476abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweetenstatic void pwm_export_release(struct device *child)
17576abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten{
17676abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten	struct pwm_export *export = child_to_pwm_export(child);
17776abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten
17876abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten	kfree(export);
17976abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten}
18076abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten
18176abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweetenstatic int pwm_export_child(struct device *parent, struct pwm_device *pwm)
18276abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten{
18376abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten	struct pwm_export *export;
18476abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten	int ret;
18576abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten
18676abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten	if (test_and_set_bit(PWMF_EXPORTED, &pwm->flags))
18776abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten		return -EBUSY;
18876abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten
18976abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten	export = kzalloc(sizeof(*export), GFP_KERNEL);
19076abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten	if (!export) {
19176abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten		clear_bit(PWMF_EXPORTED, &pwm->flags);
19276abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten		return -ENOMEM;
19376abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten	}
19476abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten
19576abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten	export->pwm = pwm;
19676abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten
19776abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten	export->child.release = pwm_export_release;
19876abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten	export->child.parent = parent;
19976abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten	export->child.devt = MKDEV(0, 0);
2006ca142ad0d1bdf8dd116351b7b9a2bc5670d5271Axel Lin	export->child.groups = pwm_groups;
20176abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten	dev_set_name(&export->child, "pwm%u", pwm->hwpwm);
20276abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten
20376abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten	ret = device_register(&export->child);
20476abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten	if (ret) {
20576abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten		clear_bit(PWMF_EXPORTED, &pwm->flags);
20676abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten		kfree(export);
20776abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten		return ret;
20876abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten	}
20976abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten
21076abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten	return 0;
21176abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten}
21276abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten
21376abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweetenstatic int pwm_unexport_match(struct device *child, void *data)
21476abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten{
21576abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten	return child_to_pwm_device(child) == data;
21676abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten}
21776abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten
21876abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweetenstatic int pwm_unexport_child(struct device *parent, struct pwm_device *pwm)
21976abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten{
22076abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten	struct device *child;
22176abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten
22276abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten	if (!test_and_clear_bit(PWMF_EXPORTED, &pwm->flags))
22376abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten		return -ENODEV;
22476abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten
22576abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten	child = device_find_child(parent, pwm, pwm_unexport_match);
22676abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten	if (!child)
22776abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten		return -ENODEV;
22876abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten
22976abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten	/* for device_find_child() */
23076abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten	put_device(child);
23176abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten	device_unregister(child);
23276abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten	pwm_put(pwm);
23376abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten
23476abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten	return 0;
23576abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten}
23676abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten
23776abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweetenstatic ssize_t pwm_export_store(struct device *parent,
23876abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten				struct device_attribute *attr,
23976abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten				const char *buf, size_t len)
24076abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten{
24176abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten	struct pwm_chip *chip = dev_get_drvdata(parent);
24276abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten	struct pwm_device *pwm;
24376abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten	unsigned int hwpwm;
24476abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten	int ret;
24576abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten
24676abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten	ret = kstrtouint(buf, 0, &hwpwm);
24776abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten	if (ret < 0)
24876abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten		return ret;
24976abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten
25076abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten	if (hwpwm >= chip->npwm)
25176abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten		return -ENODEV;
25276abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten
25376abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten	pwm = pwm_request_from_chip(chip, hwpwm, "sysfs");
25476abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten	if (IS_ERR(pwm))
25576abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten		return PTR_ERR(pwm);
25676abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten
25776abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten	ret = pwm_export_child(parent, pwm);
25876abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten	if (ret < 0)
25976abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten		pwm_put(pwm);
26076abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten
26176abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten	return ret ? : len;
26276abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten}
2639da01759636f519967c0922ae12bd9fff739db9aGreg Kroah-Hartmanstatic DEVICE_ATTR(export, 0200, NULL, pwm_export_store);
26476abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten
26576abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweetenstatic ssize_t pwm_unexport_store(struct device *parent,
26676abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten				  struct device_attribute *attr,
26776abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten				  const char *buf, size_t len)
26876abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten{
26976abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten	struct pwm_chip *chip = dev_get_drvdata(parent);
27076abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten	unsigned int hwpwm;
27176abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten	int ret;
27276abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten
27376abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten	ret = kstrtouint(buf, 0, &hwpwm);
27476abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten	if (ret < 0)
27576abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten		return ret;
27676abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten
27776abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten	if (hwpwm >= chip->npwm)
27876abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten		return -ENODEV;
27976abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten
28076abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten	ret = pwm_unexport_child(parent, &chip->pwms[hwpwm]);
28176abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten
28276abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten	return ret ? : len;
28376abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten}
2849da01759636f519967c0922ae12bd9fff739db9aGreg Kroah-Hartmanstatic DEVICE_ATTR(unexport, 0200, NULL, pwm_unexport_store);
28576abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten
2869da01759636f519967c0922ae12bd9fff739db9aGreg Kroah-Hartmanstatic ssize_t npwm_show(struct device *parent, struct device_attribute *attr,
2879da01759636f519967c0922ae12bd9fff739db9aGreg Kroah-Hartman			 char *buf)
28876abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten{
28976abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten	const struct pwm_chip *chip = dev_get_drvdata(parent);
29076abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten
29176abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten	return sprintf(buf, "%u\n", chip->npwm);
29276abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten}
2939da01759636f519967c0922ae12bd9fff739db9aGreg Kroah-Hartmanstatic DEVICE_ATTR_RO(npwm);
29476abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten
2959da01759636f519967c0922ae12bd9fff739db9aGreg Kroah-Hartmanstatic struct attribute *pwm_chip_attrs[] = {
2969da01759636f519967c0922ae12bd9fff739db9aGreg Kroah-Hartman	&dev_attr_export.attr,
2979da01759636f519967c0922ae12bd9fff739db9aGreg Kroah-Hartman	&dev_attr_unexport.attr,
2989da01759636f519967c0922ae12bd9fff739db9aGreg Kroah-Hartman	&dev_attr_npwm.attr,
2999da01759636f519967c0922ae12bd9fff739db9aGreg Kroah-Hartman	NULL,
30076abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten};
3019da01759636f519967c0922ae12bd9fff739db9aGreg Kroah-HartmanATTRIBUTE_GROUPS(pwm_chip);
30276abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten
30376abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweetenstatic struct class pwm_class = {
30476abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten	.name		= "pwm",
30576abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten	.owner		= THIS_MODULE,
3069da01759636f519967c0922ae12bd9fff739db9aGreg Kroah-Hartman	.dev_groups	= pwm_chip_groups,
30776abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten};
30876abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten
30976abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweetenstatic int pwmchip_sysfs_match(struct device *parent, const void *data)
31076abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten{
31176abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten	return dev_get_drvdata(parent) == data;
31276abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten}
31376abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten
31476abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweetenvoid pwmchip_sysfs_export(struct pwm_chip *chip)
31576abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten{
31676abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten	struct device *parent;
31776abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten
31876abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten	/*
31976abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten	 * If device_create() fails the pwm_chip is still usable by
32076abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten	 * the kernel its just not exported.
32176abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten	 */
32276abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten	parent = device_create(&pwm_class, chip->dev, MKDEV(0, 0), chip,
32376abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten			       "pwmchip%d", chip->base);
32476abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten	if (IS_ERR(parent)) {
32576abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten		dev_warn(chip->dev,
32676abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten			 "device_create failed for pwm_chip sysfs export\n");
32776abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten	}
32876abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten}
32976abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten
33076abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweetenvoid pwmchip_sysfs_unexport(struct pwm_chip *chip)
33176abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten{
33276abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten	struct device *parent;
33376abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten
33476abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten	parent = class_find_device(&pwm_class, NULL, chip,
33576abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten				   pwmchip_sysfs_match);
33676abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten	if (parent) {
33776abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten		/* for class_find_device() */
33876abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten		put_device(parent);
33976abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten		device_unregister(parent);
34076abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten	}
34176abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten}
34276abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten
34376abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweetenstatic int __init pwm_sysfs_init(void)
34476abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten{
34576abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten	return class_register(&pwm_class);
34676abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweeten}
34776abbdde2d95a3807d0dc6bf9f84d03d0dbd4f3dH Hartley Sweetensubsys_initcall(pwm_sysfs_init);
348