2013-12-06 06:18:43 +04:00
/*
* TI / National Semiconductor LP3943 PWM driver
*
* Copyright 2013 Texas Instruments
*
* Author : Milo Kim < milo . kim @ ti . com >
*
* 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 ; version 2.
*/
# include <linux/err.h>
# include <linux/i2c.h>
# include <linux/mfd/lp3943.h>
# include <linux/module.h>
# include <linux/platform_device.h>
# include <linux/pwm.h>
# include <linux/slab.h>
# define LP3943_MAX_DUTY 255
# define LP3943_MIN_PERIOD 6250
# define LP3943_MAX_PERIOD 1600000
struct lp3943_pwm {
struct pwm_chip chip ;
struct lp3943 * lp3943 ;
struct lp3943_platform_data * pdata ;
} ;
static inline struct lp3943_pwm * to_lp3943_pwm ( struct pwm_chip * _chip )
{
return container_of ( _chip , struct lp3943_pwm , chip ) ;
}
static struct lp3943_pwm_map *
lp3943_pwm_request_map ( struct lp3943_pwm * lp3943_pwm , int hwpwm )
{
struct lp3943_platform_data * pdata = lp3943_pwm - > pdata ;
struct lp3943 * lp3943 = lp3943_pwm - > lp3943 ;
struct lp3943_pwm_map * pwm_map ;
int i , offset ;
pwm_map = kzalloc ( sizeof ( * pwm_map ) , GFP_KERNEL ) ;
if ( ! pwm_map )
return ERR_PTR ( - ENOMEM ) ;
pwm_map - > output = pdata - > pwms [ hwpwm ] - > output ;
pwm_map - > num_outputs = pdata - > pwms [ hwpwm ] - > num_outputs ;
for ( i = 0 ; i < pwm_map - > num_outputs ; i + + ) {
offset = pwm_map - > output [ i ] ;
/* Return an error if the pin is already assigned */
2014-01-24 01:32:20 +04:00
if ( test_and_set_bit ( offset , & lp3943 - > pin_used ) ) {
kfree ( pwm_map ) ;
2013-12-06 06:18:43 +04:00
return ERR_PTR ( - EBUSY ) ;
2014-01-24 01:32:20 +04:00
}
2013-12-06 06:18:43 +04:00
}
return pwm_map ;
}
static int lp3943_pwm_request ( struct pwm_chip * chip , struct pwm_device * pwm )
{
struct lp3943_pwm * lp3943_pwm = to_lp3943_pwm ( chip ) ;
struct lp3943_pwm_map * pwm_map ;
pwm_map = lp3943_pwm_request_map ( lp3943_pwm , pwm - > hwpwm ) ;
if ( IS_ERR ( pwm_map ) )
return PTR_ERR ( pwm_map ) ;
return pwm_set_chip_data ( pwm , pwm_map ) ;
}
static void lp3943_pwm_free_map ( struct lp3943_pwm * lp3943_pwm ,
struct lp3943_pwm_map * pwm_map )
{
struct lp3943 * lp3943 = lp3943_pwm - > lp3943 ;
int i , offset ;
for ( i = 0 ; i < pwm_map - > num_outputs ; i + + ) {
offset = pwm_map - > output [ i ] ;
clear_bit ( offset , & lp3943 - > pin_used ) ;
}
kfree ( pwm_map ) ;
}
static void lp3943_pwm_free ( struct pwm_chip * chip , struct pwm_device * pwm )
{
struct lp3943_pwm * lp3943_pwm = to_lp3943_pwm ( chip ) ;
struct lp3943_pwm_map * pwm_map = pwm_get_chip_data ( pwm ) ;
lp3943_pwm_free_map ( lp3943_pwm , pwm_map ) ;
}
static int lp3943_pwm_config ( struct pwm_chip * chip , struct pwm_device * pwm ,
int duty_ns , int period_ns )
{
struct lp3943_pwm * lp3943_pwm = to_lp3943_pwm ( chip ) ;
struct lp3943 * lp3943 = lp3943_pwm - > lp3943 ;
u8 val , reg_duty , reg_prescale ;
int err ;
/*
* How to configure the LP3943 PWMs
*
* 1 ) Period = 6250 ~ 1600000
* 2 ) Prescale = period / 6250 - 1
* 3 ) Duty = input duty
*
* Prescale and duty are register values
*/
if ( pwm - > hwpwm = = 0 ) {
reg_prescale = LP3943_REG_PRESCALE0 ;
reg_duty = LP3943_REG_PWM0 ;
} else {
reg_prescale = LP3943_REG_PRESCALE1 ;
reg_duty = LP3943_REG_PWM1 ;
}
period_ns = clamp ( period_ns , LP3943_MIN_PERIOD , LP3943_MAX_PERIOD ) ;
val = ( u8 ) ( period_ns / LP3943_MIN_PERIOD - 1 ) ;
err = lp3943_write_byte ( lp3943 , reg_prescale , val ) ;
if ( err )
return err ;
val = ( u8 ) ( duty_ns * LP3943_MAX_DUTY / period_ns ) ;
return lp3943_write_byte ( lp3943 , reg_duty , val ) ;
}
static int lp3943_pwm_set_mode ( struct lp3943_pwm * lp3943_pwm ,
struct lp3943_pwm_map * pwm_map ,
u8 val )
{
struct lp3943 * lp3943 = lp3943_pwm - > lp3943 ;
const struct lp3943_reg_cfg * mux = lp3943 - > mux_cfg ;
int i , index , err ;
for ( i = 0 ; i < pwm_map - > num_outputs ; i + + ) {
index = pwm_map - > output [ i ] ;
err = lp3943_update_bits ( lp3943 , mux [ index ] . reg ,
mux [ index ] . mask ,
val < < mux [ index ] . shift ) ;
if ( err )
return err ;
}
return 0 ;
}
static int lp3943_pwm_enable ( struct pwm_chip * chip , struct pwm_device * pwm )
{
struct lp3943_pwm * lp3943_pwm = to_lp3943_pwm ( chip ) ;
struct lp3943_pwm_map * pwm_map = pwm_get_chip_data ( pwm ) ;
u8 val ;
if ( pwm - > hwpwm = = 0 )
val = LP3943_DIM_PWM0 ;
else
val = LP3943_DIM_PWM1 ;
/*
* Each PWM generator is set to control any of outputs of LP3943 .
* To enable / disable the PWM , these output pins should be configured .
*/
return lp3943_pwm_set_mode ( lp3943_pwm , pwm_map , val ) ;
}
static void lp3943_pwm_disable ( struct pwm_chip * chip , struct pwm_device * pwm )
{
struct lp3943_pwm * lp3943_pwm = to_lp3943_pwm ( chip ) ;
struct lp3943_pwm_map * pwm_map = pwm_get_chip_data ( pwm ) ;
/*
* LP3943 outputs are open - drain , so the pin should be configured
* when the PWM is disabled .
*/
lp3943_pwm_set_mode ( lp3943_pwm , pwm_map , LP3943_GPIO_OUT_HIGH ) ;
}
static const struct pwm_ops lp3943_pwm_ops = {
. request = lp3943_pwm_request ,
. free = lp3943_pwm_free ,
. config = lp3943_pwm_config ,
. enable = lp3943_pwm_enable ,
. disable = lp3943_pwm_disable ,
. owner = THIS_MODULE ,
} ;
static int lp3943_pwm_parse_dt ( struct device * dev ,
struct lp3943_pwm * lp3943_pwm )
{
static const char * const name [ ] = { " ti,pwm0 " , " ti,pwm1 " , } ;
struct device_node * node = dev - > of_node ;
struct lp3943_platform_data * pdata ;
struct lp3943_pwm_map * pwm_map ;
enum lp3943_pwm_output * output ;
int i , err , proplen , count = 0 ;
u32 num_outputs ;
if ( ! node )
return - EINVAL ;
pdata = devm_kzalloc ( dev , sizeof ( * pdata ) , GFP_KERNEL ) ;
if ( ! pdata )
return - ENOMEM ;
/*
* Read the output map configuration from the device tree .
* Each of the two PWM generators can drive zero or more outputs .
*/
for ( i = 0 ; i < LP3943_NUM_PWMS ; i + + ) {
if ( ! of_get_property ( node , name [ i ] , & proplen ) )
continue ;
num_outputs = proplen / sizeof ( u32 ) ;
if ( num_outputs = = 0 )
continue ;
output = devm_kzalloc ( dev , sizeof ( * output ) * num_outputs ,
GFP_KERNEL ) ;
if ( ! output )
return - ENOMEM ;
err = of_property_read_u32_array ( node , name [ i ] , output ,
num_outputs ) ;
if ( err )
return err ;
pwm_map = devm_kzalloc ( dev , sizeof ( * pwm_map ) , GFP_KERNEL ) ;
if ( ! pwm_map )
return - ENOMEM ;
pwm_map - > output = output ;
pwm_map - > num_outputs = num_outputs ;
pdata - > pwms [ i ] = pwm_map ;
count + + ;
}
if ( count = = 0 )
return - ENODATA ;
lp3943_pwm - > pdata = pdata ;
return 0 ;
}
static int lp3943_pwm_probe ( struct platform_device * pdev )
{
struct lp3943 * lp3943 = dev_get_drvdata ( pdev - > dev . parent ) ;
struct lp3943_pwm * lp3943_pwm ;
int ret ;
lp3943_pwm = devm_kzalloc ( & pdev - > dev , sizeof ( * lp3943_pwm ) , GFP_KERNEL ) ;
if ( ! lp3943_pwm )
return - ENOMEM ;
lp3943_pwm - > pdata = lp3943 - > pdata ;
if ( ! lp3943_pwm - > pdata ) {
if ( IS_ENABLED ( CONFIG_OF ) )
ret = lp3943_pwm_parse_dt ( & pdev - > dev , lp3943_pwm ) ;
else
ret = - ENODEV ;
if ( ret )
return ret ;
}
lp3943_pwm - > lp3943 = lp3943 ;
lp3943_pwm - > chip . dev = & pdev - > dev ;
lp3943_pwm - > chip . ops = & lp3943_pwm_ops ;
lp3943_pwm - > chip . npwm = LP3943_NUM_PWMS ;
2014-04-15 20:38:10 +04:00
lp3943_pwm - > chip . can_sleep = true ;
2013-12-06 06:18:43 +04:00
platform_set_drvdata ( pdev , lp3943_pwm ) ;
return pwmchip_add ( & lp3943_pwm - > chip ) ;
}
static int lp3943_pwm_remove ( struct platform_device * pdev )
{
struct lp3943_pwm * lp3943_pwm = platform_get_drvdata ( pdev ) ;
return pwmchip_remove ( & lp3943_pwm - > chip ) ;
}
# ifdef CONFIG_OF
static const struct of_device_id lp3943_pwm_of_match [ ] = {
{ . compatible = " ti,lp3943-pwm " , } ,
{ }
} ;
MODULE_DEVICE_TABLE ( of , lp3943_pwm_of_match ) ;
# endif
static struct platform_driver lp3943_pwm_driver = {
. probe = lp3943_pwm_probe ,
. remove = lp3943_pwm_remove ,
. driver = {
. name = " lp3943-pwm " ,
. of_match_table = of_match_ptr ( lp3943_pwm_of_match ) ,
} ,
} ;
module_platform_driver ( lp3943_pwm_driver ) ;
MODULE_DESCRIPTION ( " LP3943 PWM driver " ) ;
MODULE_ALIAS ( " platform:lp3943-pwm " ) ;
MODULE_AUTHOR ( " Milo Kim " ) ;
MODULE_LICENSE ( " GPL " ) ;