2010-12-23 15:11:21 +03:00
/*
2012-07-01 04:29:28 +04:00
* drivers / pwm / pwm - vt8500 . c
2010-12-23 15:11:21 +03:00
*
2012-10-27 05:49:57 +04:00
* Copyright ( C ) 2012 Tony Prisk < linux @ prisktech . co . nz >
* Copyright ( C ) 2010 Alexey Charkov < alchark @ gmail . com >
2010-12-23 15:11:21 +03:00
*
* This software is licensed under the terms of the GNU General Public
* License version 2 , as published by the Free Software Foundation , and
* may be copied , distributed , and modified under those terms .
*
* 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/module.h>
# include <linux/kernel.h>
# include <linux/platform_device.h>
# include <linux/slab.h>
# include <linux/err.h>
# include <linux/io.h>
# include <linux/pwm.h>
# include <linux/delay.h>
2012-10-27 05:49:57 +04:00
# include <linux/clk.h>
2010-12-23 15:11:21 +03:00
# include <asm/div64.h>
2012-10-27 05:49:57 +04:00
# include <linux/of.h>
# include <linux/of_device.h>
# include <linux/of_address.h>
/*
* SoC architecture allocates register space for 4 PWMs but only
* 2 are currently implemented .
*/
# define VT8500_NR_PWMS 2
2010-12-23 15:11:21 +03:00
2013-01-02 23:44:15 +04:00
# define REG_CTRL(pwm) (((pwm) << 4) + 0x00)
# define REG_SCALAR(pwm) (((pwm) << 4) + 0x04)
# define REG_PERIOD(pwm) (((pwm) << 4) + 0x08)
# define REG_DUTY(pwm) (((pwm) << 4) + 0x0C)
# define REG_STATUS 0x40
# define CTRL_ENABLE BIT(0)
# define CTRL_INVERT BIT(1)
# define CTRL_AUTOLOAD BIT(2)
# define CTRL_STOP_IMM BIT(3)
# define CTRL_LOAD_PRESCALE BIT(4)
# define CTRL_LOAD_PERIOD BIT(5)
# define STATUS_CTRL_UPDATE BIT(0)
# define STATUS_SCALAR_UPDATE BIT(1)
# define STATUS_PERIOD_UPDATE BIT(2)
# define STATUS_DUTY_UPDATE BIT(3)
# define STATUS_ALL_UPDATE 0x0F
2012-03-15 13:04:37 +04:00
struct vt8500_chip {
struct pwm_chip chip ;
void __iomem * base ;
2012-10-27 05:49:57 +04:00
struct clk * clk ;
2010-12-23 15:11:21 +03:00
} ;
2012-03-15 13:04:37 +04:00
# define to_vt8500_chip(chip) container_of(chip, struct vt8500_chip, chip)
2010-12-23 15:11:21 +03:00
# define msecs_to_loops(t) (loops_per_jiffy / 1000 * HZ * t)
2013-01-02 23:44:15 +04:00
static inline void pwm_busy_wait ( struct vt8500_chip * vt8500 , int nr , u8 bitmask )
2010-12-23 15:11:21 +03:00
{
int loops = msecs_to_loops ( 10 ) ;
2013-01-02 23:44:15 +04:00
u32 mask = bitmask < < ( nr < < 8 ) ;
while ( ( readl ( vt8500 - > base + REG_STATUS ) & mask ) & & - - loops )
2010-12-23 15:11:21 +03:00
cpu_relax ( ) ;
if ( unlikely ( ! loops ) )
2013-01-02 23:44:15 +04:00
dev_warn ( vt8500 - > chip . dev , " Waiting for status bits 0x%x to clear timed out \n " ,
mask ) ;
2010-12-23 15:11:21 +03:00
}
2012-03-15 13:04:37 +04:00
static int vt8500_pwm_config ( struct pwm_chip * chip , struct pwm_device * pwm ,
int duty_ns , int period_ns )
2010-12-23 15:11:21 +03:00
{
2012-03-15 13:04:37 +04:00
struct vt8500_chip * vt8500 = to_vt8500_chip ( chip ) ;
2010-12-23 15:11:21 +03:00
unsigned long long c ;
unsigned long period_cycles , prescale , pv , dc ;
2012-11-19 21:44:46 +04:00
int err ;
2013-01-02 23:44:15 +04:00
u32 val ;
2012-11-19 21:44:46 +04:00
err = clk_enable ( vt8500 - > clk ) ;
if ( err < 0 ) {
dev_err ( chip - > dev , " failed to enable clock \n " ) ;
return err ;
}
2010-12-23 15:11:21 +03:00
2012-10-27 05:49:57 +04:00
c = clk_get_rate ( vt8500 - > clk ) ;
2010-12-23 15:11:21 +03:00
c = c * period_ns ;
do_div ( c , 1000000000 ) ;
period_cycles = c ;
if ( period_cycles < 1 )
period_cycles = 1 ;
prescale = ( period_cycles - 1 ) / 4096 ;
pv = period_cycles / ( prescale + 1 ) - 1 ;
if ( pv > 4095 )
pv = 4095 ;
2012-11-19 21:44:46 +04:00
if ( prescale > 1023 ) {
clk_disable ( vt8500 - > clk ) ;
2010-12-23 15:11:21 +03:00
return - EINVAL ;
2012-11-19 21:44:46 +04:00
}
2010-12-23 15:11:21 +03:00
c = ( unsigned long long ) pv * duty_ns ;
do_div ( c , period_ns ) ;
dc = c ;
2013-01-02 23:44:15 +04:00
writel ( prescale , vt8500 - > base + REG_SCALAR ( pwm - > hwpwm ) ) ;
pwm_busy_wait ( vt8500 , pwm - > hwpwm , STATUS_SCALAR_UPDATE ) ;
2010-12-23 15:11:21 +03:00
2013-01-02 23:44:15 +04:00
writel ( pv , vt8500 - > base + REG_PERIOD ( pwm - > hwpwm ) ) ;
pwm_busy_wait ( vt8500 , pwm - > hwpwm , STATUS_PERIOD_UPDATE ) ;
2010-12-23 15:11:21 +03:00
2013-01-02 23:44:15 +04:00
writel ( dc , vt8500 - > base + REG_DUTY ( pwm - > hwpwm ) ) ;
pwm_busy_wait ( vt8500 , pwm - > hwpwm , STATUS_DUTY_UPDATE ) ;
2010-12-23 15:11:21 +03:00
2013-01-02 23:44:15 +04:00
val = readl ( vt8500 - > base + REG_CTRL ( pwm - > hwpwm ) ) ;
val | = CTRL_AUTOLOAD ;
writel ( val , vt8500 - > base + REG_CTRL ( pwm - > hwpwm ) ) ;
pwm_busy_wait ( vt8500 , pwm - > hwpwm , STATUS_CTRL_UPDATE ) ;
2010-12-23 15:11:21 +03:00
2012-11-19 21:44:46 +04:00
clk_disable ( vt8500 - > clk ) ;
2010-12-23 15:11:21 +03:00
return 0 ;
}
2012-03-15 13:04:37 +04:00
static int vt8500_pwm_enable ( struct pwm_chip * chip , struct pwm_device * pwm )
2010-12-23 15:11:21 +03:00
{
2012-03-15 13:04:37 +04:00
struct vt8500_chip * vt8500 = to_vt8500_chip ( chip ) ;
2013-01-02 23:44:15 +04:00
int err ;
u32 val ;
2010-12-23 15:11:21 +03:00
2012-10-27 05:49:57 +04:00
err = clk_enable ( vt8500 - > clk ) ;
2012-11-19 21:44:45 +04:00
if ( err < 0 ) {
2012-10-27 05:49:57 +04:00
dev_err ( chip - > dev , " failed to enable clock \n " ) ;
return err ;
2012-11-19 21:44:46 +04:00
}
2012-10-27 05:49:57 +04:00
2013-01-02 23:44:15 +04:00
val = readl ( vt8500 - > base + REG_CTRL ( pwm - > hwpwm ) ) ;
val | = CTRL_ENABLE ;
writel ( val , vt8500 - > base + REG_CTRL ( pwm - > hwpwm ) ) ;
pwm_busy_wait ( vt8500 , pwm - > hwpwm , STATUS_CTRL_UPDATE ) ;
2012-03-15 13:04:37 +04:00
return 0 ;
2010-12-23 15:11:21 +03:00
}
2012-03-15 13:04:37 +04:00
static void vt8500_pwm_disable ( struct pwm_chip * chip , struct pwm_device * pwm )
2010-12-23 15:11:21 +03:00
{
2012-03-15 13:04:37 +04:00
struct vt8500_chip * vt8500 = to_vt8500_chip ( chip ) ;
2013-01-02 23:44:15 +04:00
u32 val ;
2010-12-23 15:11:21 +03:00
2013-01-02 23:44:15 +04:00
val = readl ( vt8500 - > base + REG_CTRL ( pwm - > hwpwm ) ) ;
val & = ~ CTRL_ENABLE ;
writel ( val , vt8500 - > base + REG_CTRL ( pwm - > hwpwm ) ) ;
pwm_busy_wait ( vt8500 , pwm - > hwpwm , STATUS_CTRL_UPDATE ) ;
2012-10-27 05:49:57 +04:00
clk_disable ( vt8500 - > clk ) ;
2010-12-23 15:11:21 +03:00
}
2013-01-02 23:44:16 +04:00
static int vt8500_pwm_set_polarity ( struct pwm_chip * chip ,
struct pwm_device * pwm ,
enum pwm_polarity polarity )
{
struct vt8500_chip * vt8500 = to_vt8500_chip ( chip ) ;
u32 val ;
val = readl ( vt8500 - > base + REG_CTRL ( pwm - > hwpwm ) ) ;
if ( polarity = = PWM_POLARITY_INVERSED )
val | = CTRL_INVERT ;
else
val & = ~ CTRL_INVERT ;
writel ( val , vt8500 - > base + REG_CTRL ( pwm - > hwpwm ) ) ;
pwm_busy_wait ( vt8500 , pwm - > hwpwm , STATUS_CTRL_UPDATE ) ;
return 0 ;
}
2012-03-15 13:04:37 +04:00
static struct pwm_ops vt8500_pwm_ops = {
. enable = vt8500_pwm_enable ,
. disable = vt8500_pwm_disable ,
. config = vt8500_pwm_config ,
2013-01-02 23:44:16 +04:00
. set_polarity = vt8500_pwm_set_polarity ,
2012-03-15 13:04:37 +04:00
. owner = THIS_MODULE ,
} ;
2010-12-23 15:11:21 +03:00
2012-10-27 05:49:57 +04:00
static const struct of_device_id vt8500_pwm_dt_ids [ ] = {
{ . compatible = " via,vt8500-pwm " , } ,
{ /* Sentinel */ }
} ;
MODULE_DEVICE_TABLE ( of , vt8500_pwm_dt_ids ) ;
static int vt8500_pwm_probe ( struct platform_device * pdev )
2010-12-23 15:11:21 +03:00
{
2012-03-15 13:04:37 +04:00
struct vt8500_chip * chip ;
2010-12-23 15:11:21 +03:00
struct resource * r ;
2012-10-27 05:49:57 +04:00
struct device_node * np = pdev - > dev . of_node ;
2012-03-15 13:04:37 +04:00
int ret ;
2010-12-23 15:11:21 +03:00
2012-10-27 05:49:57 +04:00
if ( ! np ) {
dev_err ( & pdev - > dev , " invalid devicetree node \n " ) ;
return - EINVAL ;
}
2012-07-01 04:29:28 +04:00
chip = devm_kzalloc ( & pdev - > dev , sizeof ( * chip ) , GFP_KERNEL ) ;
2014-04-23 13:42:10 +04:00
if ( chip = = NULL )
2010-12-23 15:11:21 +03:00
return - ENOMEM ;
2012-03-15 13:04:37 +04:00
chip - > chip . dev = & pdev - > dev ;
chip - > chip . ops = & vt8500_pwm_ops ;
2013-01-02 23:44:16 +04:00
chip - > chip . of_xlate = of_pwm_xlate_with_flags ;
chip - > chip . of_pwm_n_cells = 3 ;
2012-03-15 13:04:37 +04:00
chip - > chip . base = - 1 ;
chip - > chip . npwm = VT8500_NR_PWMS ;
2010-12-23 15:11:21 +03:00
2012-10-27 05:49:57 +04:00
chip - > clk = devm_clk_get ( & pdev - > dev , NULL ) ;
if ( IS_ERR ( chip - > clk ) ) {
dev_err ( & pdev - > dev , " clock source not specified \n " ) ;
return PTR_ERR ( chip - > clk ) ;
}
2010-12-23 15:11:21 +03:00
r = platform_get_resource ( pdev , IORESOURCE_MEM , 0 ) ;
2013-01-21 14:09:16 +04:00
chip - > base = devm_ioremap_resource ( & pdev - > dev , r ) ;
if ( IS_ERR ( chip - > base ) )
return PTR_ERR ( chip - > base ) ;
2010-12-23 15:11:21 +03:00
2012-10-27 05:49:57 +04:00
ret = clk_prepare ( chip - > clk ) ;
if ( ret < 0 ) {
dev_err ( & pdev - > dev , " failed to prepare clock \n " ) ;
return ret ;
}
2012-03-15 13:04:37 +04:00
ret = pwmchip_add ( & chip - > chip ) ;
2012-10-27 05:49:57 +04:00
if ( ret < 0 ) {
dev_err ( & pdev - > dev , " failed to add PWM chip \n " ) ;
2012-07-01 04:29:28 +04:00
return ret ;
2012-10-27 05:49:57 +04:00
}
2010-12-23 15:11:21 +03:00
2012-03-15 13:04:37 +04:00
platform_set_drvdata ( pdev , chip ) ;
return ret ;
2010-12-23 15:11:21 +03:00
}
2012-10-27 05:49:57 +04:00
static int vt8500_pwm_remove ( struct platform_device * pdev )
2010-12-23 15:11:21 +03:00
{
2012-03-15 13:04:37 +04:00
struct vt8500_chip * chip ;
2010-12-23 15:11:21 +03:00
2012-03-15 13:04:37 +04:00
chip = platform_get_drvdata ( pdev ) ;
if ( chip = = NULL )
2010-12-23 15:11:21 +03:00
return - ENODEV ;
2012-10-27 05:49:57 +04:00
clk_unprepare ( chip - > clk ) ;
2012-07-01 04:29:28 +04:00
return pwmchip_remove ( & chip - > chip ) ;
2010-12-23 15:11:21 +03:00
}
2012-10-27 05:49:57 +04:00
static struct platform_driver vt8500_pwm_driver = {
. probe = vt8500_pwm_probe ,
. remove = vt8500_pwm_remove ,
2010-12-23 15:11:21 +03:00
. driver = {
. name = " vt8500-pwm " ,
2012-10-27 05:49:57 +04:00
. of_match_table = vt8500_pwm_dt_ids ,
2010-12-23 15:11:21 +03:00
} ,
} ;
2012-10-27 05:49:57 +04:00
module_platform_driver ( vt8500_pwm_driver ) ;
2010-12-23 15:11:21 +03:00
2012-10-27 05:49:57 +04:00
MODULE_DESCRIPTION ( " VT8500 PWM Driver " ) ;
MODULE_AUTHOR ( " Tony Prisk <linux@prisktech.co.nz> " ) ;
MODULE_LICENSE ( " GPL v2 " ) ;