2013-06-11 21:38:59 +04:00
/*
* 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 ;
2016-03-30 23:03:27 +03:00
struct mutex lock ;
2013-06-11 21:38:59 +04:00
} ;
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 ;
}
2015-10-27 00:32:37 +03:00
static ssize_t period_show ( struct device * child ,
struct device_attribute * attr ,
char * buf )
2013-06-11 21:38:59 +04:00
{
const struct pwm_device * pwm = child_to_pwm_device ( child ) ;
2016-04-14 22:17:43 +03:00
struct pwm_state state ;
2013-06-11 21:38:59 +04:00
2016-04-14 22:17:43 +03:00
pwm_get_state ( pwm , & state ) ;
return sprintf ( buf , " %u \n " , state . period ) ;
2013-06-11 21:38:59 +04:00
}
2015-10-27 00:32:37 +03:00
static ssize_t period_store ( struct device * child ,
struct device_attribute * attr ,
const char * buf , size_t size )
2013-06-11 21:38:59 +04:00
{
2016-03-30 23:03:27 +03:00
struct pwm_export * export = child_to_pwm_export ( child ) ;
struct pwm_device * pwm = export - > pwm ;
2016-04-14 22:17:43 +03:00
struct pwm_state state ;
2013-06-11 21:38:59 +04:00
unsigned int val ;
int ret ;
ret = kstrtouint ( buf , 0 , & val ) ;
if ( ret )
return ret ;
2016-03-30 23:03:27 +03:00
mutex_lock ( & export - > lock ) ;
2016-04-14 22:17:43 +03:00
pwm_get_state ( pwm , & state ) ;
state . period = val ;
ret = pwm_apply_state ( pwm , & state ) ;
2016-03-30 23:03:27 +03:00
mutex_unlock ( & export - > lock ) ;
2013-06-11 21:38:59 +04:00
return ret ? : size ;
}
2015-10-27 00:32:37 +03:00
static ssize_t duty_cycle_show ( struct device * child ,
struct device_attribute * attr ,
char * buf )
2013-06-11 21:38:59 +04:00
{
const struct pwm_device * pwm = child_to_pwm_device ( child ) ;
2016-04-14 22:17:43 +03:00
struct pwm_state state ;
pwm_get_state ( pwm , & state ) ;
2013-06-11 21:38:59 +04:00
2016-04-14 22:17:43 +03:00
return sprintf ( buf , " %u \n " , state . duty_cycle ) ;
2013-06-11 21:38:59 +04:00
}
2015-10-27 00:32:37 +03:00
static ssize_t duty_cycle_store ( struct device * child ,
struct device_attribute * attr ,
const char * buf , size_t size )
2013-06-11 21:38:59 +04:00
{
2016-03-30 23:03:27 +03:00
struct pwm_export * export = child_to_pwm_export ( child ) ;
struct pwm_device * pwm = export - > pwm ;
2016-04-14 22:17:43 +03:00
struct pwm_state state ;
2013-06-11 21:38:59 +04:00
unsigned int val ;
int ret ;
ret = kstrtouint ( buf , 0 , & val ) ;
if ( ret )
return ret ;
2016-03-30 23:03:27 +03:00
mutex_lock ( & export - > lock ) ;
2016-04-14 22:17:43 +03:00
pwm_get_state ( pwm , & state ) ;
state . duty_cycle = val ;
ret = pwm_apply_state ( pwm , & state ) ;
2016-03-30 23:03:27 +03:00
mutex_unlock ( & export - > lock ) ;
2013-06-11 21:38:59 +04:00
return ret ? : size ;
}
2015-10-27 00:32:37 +03:00
static ssize_t enable_show ( struct device * child ,
struct device_attribute * attr ,
char * buf )
2013-06-11 21:38:59 +04:00
{
const struct pwm_device * pwm = child_to_pwm_device ( child ) ;
2016-04-14 22:17:43 +03:00
struct pwm_state state ;
2013-06-11 21:38:59 +04:00
2016-04-14 22:17:43 +03:00
pwm_get_state ( pwm , & state ) ;
return sprintf ( buf , " %d \n " , state . enabled ) ;
2013-06-11 21:38:59 +04:00
}
2015-10-27 00:32:37 +03:00
static ssize_t enable_store ( struct device * child ,
struct device_attribute * attr ,
const char * buf , size_t size )
2013-06-11 21:38:59 +04:00
{
2016-03-30 23:03:27 +03:00
struct pwm_export * export = child_to_pwm_export ( child ) ;
struct pwm_device * pwm = export - > pwm ;
2016-04-14 22:17:43 +03:00
struct pwm_state state ;
2013-06-11 21:38:59 +04:00
int val , ret ;
ret = kstrtoint ( buf , 0 , & val ) ;
if ( ret )
return ret ;
2016-03-30 23:03:27 +03:00
mutex_lock ( & export - > lock ) ;
2016-04-14 22:17:43 +03:00
pwm_get_state ( pwm , & state ) ;
2013-06-11 21:38:59 +04:00
switch ( val ) {
case 0 :
2016-04-14 22:17:43 +03:00
state . enabled = false ;
2013-06-11 21:38:59 +04:00
break ;
case 1 :
2016-04-14 22:17:43 +03:00
state . enabled = true ;
2013-06-11 21:38:59 +04:00
break ;
default :
ret = - EINVAL ;
2016-04-14 22:17:43 +03:00
goto unlock ;
2013-06-11 21:38:59 +04:00
}
2016-06-08 04:58:23 +03:00
ret = pwm_apply_state ( pwm , & state ) ;
2016-03-30 23:03:27 +03:00
2016-04-14 22:17:43 +03:00
unlock :
mutex_unlock ( & export - > lock ) ;
2013-06-11 21:38:59 +04:00
return ret ? : size ;
}
2015-10-27 00:32:37 +03:00
static ssize_t polarity_show ( struct device * child ,
struct device_attribute * attr ,
char * buf )
2013-06-11 21:38:59 +04:00
{
const struct pwm_device * pwm = child_to_pwm_device ( child ) ;
2015-07-20 10:56:05 +03:00
const char * polarity = " unknown " ;
2016-04-14 22:17:43 +03:00
struct pwm_state state ;
pwm_get_state ( pwm , & state ) ;
2013-06-11 21:38:59 +04:00
2016-04-14 22:17:43 +03:00
switch ( state . polarity ) {
2015-07-20 10:56:05 +03:00
case PWM_POLARITY_NORMAL :
polarity = " normal " ;
break ;
case PWM_POLARITY_INVERSED :
polarity = " inversed " ;
break ;
}
return sprintf ( buf , " %s \n " , polarity ) ;
2013-06-11 21:38:59 +04:00
}
2015-10-27 00:32:37 +03:00
static ssize_t polarity_store ( struct device * child ,
struct device_attribute * attr ,
const char * buf , size_t size )
2013-06-11 21:38:59 +04:00
{
2016-03-30 23:03:27 +03:00
struct pwm_export * export = child_to_pwm_export ( child ) ;
struct pwm_device * pwm = export - > pwm ;
2013-06-11 21:38:59 +04:00
enum pwm_polarity polarity ;
2016-04-14 22:17:43 +03:00
struct pwm_state state ;
2013-06-11 21:38:59 +04:00
int ret ;
if ( sysfs_streq ( buf , " normal " ) )
polarity = PWM_POLARITY_NORMAL ;
else if ( sysfs_streq ( buf , " inversed " ) )
polarity = PWM_POLARITY_INVERSED ;
else
return - EINVAL ;
2016-03-30 23:03:27 +03:00
mutex_lock ( & export - > lock ) ;
2016-04-14 22:17:43 +03:00
pwm_get_state ( pwm , & state ) ;
state . polarity = polarity ;
ret = pwm_apply_state ( pwm , & state ) ;
2016-03-30 23:03:27 +03:00
mutex_unlock ( & export - > lock ) ;
2013-06-11 21:38:59 +04:00
return ret ? : size ;
}
2016-06-08 12:21:25 +03:00
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 \n " , result . period , result . duty_cycle ) ;
}
2015-10-27 00:32:37 +03:00
static DEVICE_ATTR_RW ( period ) ;
static DEVICE_ATTR_RW ( duty_cycle ) ;
static DEVICE_ATTR_RW ( enable ) ;
static DEVICE_ATTR_RW ( polarity ) ;
2016-06-08 12:21:25 +03:00
static DEVICE_ATTR_RO ( capture ) ;
2013-06-11 21:38:59 +04:00
static struct attribute * pwm_attrs [ ] = {
& dev_attr_period . attr ,
& dev_attr_duty_cycle . attr ,
& dev_attr_enable . attr ,
& dev_attr_polarity . attr ,
2016-06-08 12:21:25 +03:00
& dev_attr_capture . attr ,
2013-06-11 21:38:59 +04:00
NULL
} ;
2013-12-04 14:29:53 +04:00
ATTRIBUTE_GROUPS ( pwm ) ;
2013-06-11 21:38:59 +04:00
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 ;
2016-03-30 23:03:27 +03:00
mutex_init ( & export - > lock ) ;
2013-06-11 21:38:59 +04:00
2017-09-26 14:59:51 +03:00
export - > child . class = parent - > class ;
2013-06-11 21:38:59 +04:00
export - > child . release = pwm_export_release ;
export - > child . parent = parent ;
export - > child . devt = MKDEV ( 0 , 0 ) ;
2013-12-04 14:29:53 +04:00
export - > child . groups = pwm_groups ;
2013-06-11 21:38:59 +04:00
dev_set_name ( & export - > child , " pwm%u " , pwm - > hwpwm ) ;
ret = device_register ( & export - > child ) ;
if ( ret ) {
clear_bit ( PWMF_EXPORTED , & pwm - > flags ) ;
2018-03-08 12:57:37 +03:00
put_device ( & export - > child ) ;
export = NULL ;
2013-06-11 21:38:59 +04:00
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 ;
}
2015-10-27 00:32:37 +03:00
static ssize_t export_store ( struct device * parent ,
struct device_attribute * attr ,
const char * buf , size_t len )
2013-06-11 21:38:59 +04:00
{
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 ;
}
2015-10-27 00:32:37 +03:00
static DEVICE_ATTR_WO ( export ) ;
2013-06-11 21:38:59 +04:00
2015-10-27 00:32:37 +03:00
static ssize_t unexport_store ( struct device * parent ,
struct device_attribute * attr ,
const char * buf , size_t len )
2013-06-11 21:38:59 +04:00
{
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 ;
}
2015-10-27 00:32:37 +03:00
static DEVICE_ATTR_WO ( unexport ) ;
2013-06-11 21:38:59 +04:00
2013-07-25 02:05:39 +04:00
static ssize_t npwm_show ( struct device * parent , struct device_attribute * attr ,
char * buf )
2013-06-11 21:38:59 +04:00
{
const struct pwm_chip * chip = dev_get_drvdata ( parent ) ;
return sprintf ( buf , " %u \n " , chip - > npwm ) ;
}
2013-07-25 02:05:39 +04:00
static DEVICE_ATTR_RO ( npwm ) ;
2013-06-11 21:38:59 +04:00
2013-07-25 02:05:39 +04:00
static struct attribute * pwm_chip_attrs [ ] = {
& dev_attr_export . attr ,
& dev_attr_unexport . attr ,
& dev_attr_npwm . attr ,
NULL ,
2013-06-11 21:38:59 +04:00
} ;
2013-07-25 02:05:39 +04:00
ATTRIBUTE_GROUPS ( pwm_chip ) ;
2013-06-11 21:38:59 +04:00
static struct class pwm_class = {
2015-07-20 10:58:09 +03:00
. name = " pwm " ,
. owner = THIS_MODULE ,
. dev_groups = pwm_chip_groups ,
2013-06-11 21:38:59 +04:00
} ;
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 \n " ) ;
}
}
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 ) ;
}
}
2016-08-10 00:57:46 +03:00
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 ) ;
}
2016-11-01 13:46:39 +03:00
put_device ( parent ) ;
2016-08-10 00:57:46 +03:00
}
2013-06-11 21:38:59 +04:00
static int __init pwm_sysfs_init ( void )
{
return class_register ( & pwm_class ) ;
}
subsys_initcall ( pwm_sysfs_init ) ;