2014-10-08 12:14:32 +02:00
/*
* Copyright 2014 Bart Tanghe < bart . tanghe @ thomasmore . be >
*
* 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/clk.h>
# include <linux/err.h>
# include <linux/io.h>
# include <linux/module.h>
# include <linux/of.h>
# include <linux/platform_device.h>
# include <linux/pwm.h>
# define PWM_CONTROL 0x000
# define PWM_CONTROL_SHIFT(x) ((x) * 8)
# define PWM_CONTROL_MASK 0xff
# define PWM_MODE 0x80 /* set timer in PWM mode */
# define PWM_ENABLE (1 << 0)
# define PWM_POLARITY (1 << 4)
# define PERIOD(x) (((x) * 0x10) + 0x10)
# define DUTY(x) (((x) * 0x10) + 0x14)
# define MIN_PERIOD 108 /* 9.2 MHz max. PWM clock */
struct bcm2835_pwm {
struct pwm_chip chip ;
struct device * dev ;
void __iomem * base ;
struct clk * clk ;
} ;
static inline struct bcm2835_pwm * to_bcm2835_pwm ( struct pwm_chip * chip )
{
return container_of ( chip , struct bcm2835_pwm , chip ) ;
}
static int bcm2835_pwm_request ( struct pwm_chip * chip , struct pwm_device * pwm )
{
struct bcm2835_pwm * pc = to_bcm2835_pwm ( chip ) ;
u32 value ;
value = readl ( pc - > base + PWM_CONTROL ) ;
value & = ~ ( PWM_CONTROL_MASK < < PWM_CONTROL_SHIFT ( pwm - > hwpwm ) ) ;
value | = ( PWM_MODE < < PWM_CONTROL_SHIFT ( pwm - > hwpwm ) ) ;
writel ( value , pc - > base + PWM_CONTROL ) ;
return 0 ;
}
static void bcm2835_pwm_free ( struct pwm_chip * chip , struct pwm_device * pwm )
{
struct bcm2835_pwm * pc = to_bcm2835_pwm ( chip ) ;
u32 value ;
value = readl ( pc - > base + PWM_CONTROL ) ;
value & = ~ ( PWM_CONTROL_MASK < < PWM_CONTROL_SHIFT ( pwm - > hwpwm ) ) ;
writel ( value , pc - > base + PWM_CONTROL ) ;
}
static int bcm2835_pwm_config ( struct pwm_chip * chip , struct pwm_device * pwm ,
int duty_ns , int period_ns )
{
struct bcm2835_pwm * pc = to_bcm2835_pwm ( chip ) ;
2015-12-01 22:55:40 +00:00
unsigned long rate = clk_get_rate ( pc - > clk ) ;
unsigned long scaler ;
if ( ! rate ) {
dev_err ( pc - > dev , " failed to get clock rate \n " ) ;
return - EINVAL ;
}
scaler = NSEC_PER_SEC / rate ;
2014-10-08 12:14:32 +02:00
if ( period_ns < = MIN_PERIOD ) {
dev_err ( pc - > dev , " period %d not supported, minimum %d \n " ,
period_ns , MIN_PERIOD ) ;
return - EINVAL ;
}
2015-12-01 22:55:39 +00:00
writel ( duty_ns / scaler , pc - > base + DUTY ( pwm - > hwpwm ) ) ;
writel ( period_ns / scaler , pc - > base + PERIOD ( pwm - > hwpwm ) ) ;
2014-10-08 12:14:32 +02:00
return 0 ;
}
static int bcm2835_pwm_enable ( struct pwm_chip * chip , struct pwm_device * pwm )
{
struct bcm2835_pwm * pc = to_bcm2835_pwm ( chip ) ;
u32 value ;
value = readl ( pc - > base + PWM_CONTROL ) ;
value | = PWM_ENABLE < < PWM_CONTROL_SHIFT ( pwm - > hwpwm ) ;
writel ( value , pc - > base + PWM_CONTROL ) ;
return 0 ;
}
static void bcm2835_pwm_disable ( struct pwm_chip * chip , struct pwm_device * pwm )
{
struct bcm2835_pwm * pc = to_bcm2835_pwm ( chip ) ;
u32 value ;
value = readl ( pc - > base + PWM_CONTROL ) ;
value & = ~ ( PWM_ENABLE < < PWM_CONTROL_SHIFT ( pwm - > hwpwm ) ) ;
writel ( value , pc - > base + PWM_CONTROL ) ;
}
static int bcm2835_set_polarity ( struct pwm_chip * chip , struct pwm_device * pwm ,
enum pwm_polarity polarity )
{
struct bcm2835_pwm * pc = to_bcm2835_pwm ( chip ) ;
u32 value ;
value = readl ( pc - > base + PWM_CONTROL ) ;
if ( polarity = = PWM_POLARITY_NORMAL )
value & = ~ ( PWM_POLARITY < < PWM_CONTROL_SHIFT ( pwm - > hwpwm ) ) ;
else
value | = PWM_POLARITY < < PWM_CONTROL_SHIFT ( pwm - > hwpwm ) ;
writel ( value , pc - > base + PWM_CONTROL ) ;
return 0 ;
}
static const struct pwm_ops bcm2835_pwm_ops = {
. request = bcm2835_pwm_request ,
. free = bcm2835_pwm_free ,
. config = bcm2835_pwm_config ,
. enable = bcm2835_pwm_enable ,
. disable = bcm2835_pwm_disable ,
. set_polarity = bcm2835_set_polarity ,
. owner = THIS_MODULE ,
} ;
static int bcm2835_pwm_probe ( struct platform_device * pdev )
{
struct bcm2835_pwm * pc ;
struct resource * res ;
int ret ;
pc = devm_kzalloc ( & pdev - > dev , sizeof ( * pc ) , GFP_KERNEL ) ;
if ( ! pc )
return - ENOMEM ;
pc - > dev = & pdev - > dev ;
res = platform_get_resource ( pdev , IORESOURCE_MEM , 0 ) ;
pc - > base = devm_ioremap_resource ( & pdev - > dev , res ) ;
if ( IS_ERR ( pc - > base ) )
return PTR_ERR ( pc - > base ) ;
pc - > clk = devm_clk_get ( & pdev - > dev , NULL ) ;
if ( IS_ERR ( pc - > clk ) ) {
dev_err ( & pdev - > dev , " clock not found: %ld \n " , PTR_ERR ( pc - > clk ) ) ;
return PTR_ERR ( pc - > clk ) ;
}
ret = clk_prepare_enable ( pc - > clk ) ;
if ( ret )
return ret ;
pc - > chip . dev = & pdev - > dev ;
pc - > chip . ops = & bcm2835_pwm_ops ;
pc - > chip . npwm = 2 ;
2017-08-12 12:19:44 +02:00
pc - > chip . of_xlate = of_pwm_xlate_with_flags ;
pc - > chip . of_pwm_n_cells = 3 ;
2014-10-08 12:14:32 +02:00
platform_set_drvdata ( pdev , pc ) ;
ret = pwmchip_add ( & pc - > chip ) ;
if ( ret < 0 )
goto add_fail ;
return 0 ;
add_fail :
clk_disable_unprepare ( pc - > clk ) ;
return ret ;
}
static int bcm2835_pwm_remove ( struct platform_device * pdev )
{
struct bcm2835_pwm * pc = platform_get_drvdata ( pdev ) ;
clk_disable_unprepare ( pc - > clk ) ;
return pwmchip_remove ( & pc - > chip ) ;
}
static const struct of_device_id bcm2835_pwm_of_match [ ] = {
{ . compatible = " brcm,bcm2835-pwm " , } ,
{ /* sentinel */ }
} ;
MODULE_DEVICE_TABLE ( of , bcm2835_pwm_of_match ) ;
static struct platform_driver bcm2835_pwm_driver = {
. driver = {
. name = " bcm2835-pwm " ,
. of_match_table = bcm2835_pwm_of_match ,
} ,
. probe = bcm2835_pwm_probe ,
. remove = bcm2835_pwm_remove ,
} ;
module_platform_driver ( bcm2835_pwm_driver ) ;
2015-12-01 22:55:41 +00:00
MODULE_AUTHOR ( " Bart Tanghe <bart.tanghe@thomasmore.be> " ) ;
2014-10-08 12:14:32 +02:00
MODULE_DESCRIPTION ( " Broadcom BCM2835 PWM driver " ) ;
MODULE_LICENSE ( " GPL v2 " ) ;