2019-05-28 19:57:18 +03:00
// SPDX-License-Identifier: GPL-2.0-only
2014-12-18 00:15:39 +03:00
/*
* Driver for Allwinner sun4i Pulse Width Modulation Controller
*
* Copyright ( C ) 2014 Alexandre Belloni < alexandre . belloni @ free - electrons . com >
2019-11-24 20:29:07 +03:00
*
* Limitations :
* - When outputing the source clock directly , the PWM logic will be bypassed
* and the currently running period is not guaranteed to be completed
2014-12-18 00:15:39 +03:00
*/
# include <linux/bitops.h>
# include <linux/clk.h>
2017-05-30 22:32:08 +03:00
# include <linux/delay.h>
2014-12-18 00:15:39 +03:00
# include <linux/err.h>
# include <linux/io.h>
2017-05-30 22:32:08 +03:00
# include <linux/jiffies.h>
2014-12-18 00:15:39 +03:00
# include <linux/module.h>
# include <linux/of.h>
# include <linux/of_device.h>
# include <linux/platform_device.h>
# include <linux/pwm.h>
2019-11-24 20:29:03 +03:00
# include <linux/reset.h>
2014-12-18 00:15:39 +03:00
# include <linux/slab.h>
# include <linux/spinlock.h>
# include <linux/time.h>
# define PWM_CTRL_REG 0x0
# define PWM_CH_PRD_BASE 0x4
# define PWM_CH_PRD_OFFSET 0x4
# define PWM_CH_PRD(ch) (PWM_CH_PRD_BASE + PWM_CH_PRD_OFFSET * (ch))
# define PWMCH_OFFSET 15
# define PWM_PRESCAL_MASK GENMASK(3, 0)
# define PWM_PRESCAL_OFF 0
# define PWM_EN BIT(4)
# define PWM_ACT_STATE BIT(5)
# define PWM_CLK_GATING BIT(6)
# define PWM_MODE BIT(7)
# define PWM_PULSE BIT(8)
# define PWM_BYPASS BIT(9)
# define PWM_RDY_BASE 28
# define PWM_RDY_OFFSET 1
# define PWM_RDY(ch) BIT(PWM_RDY_BASE + PWM_RDY_OFFSET * (ch))
# define PWM_PRD(prd) (((prd) - 1) << 16)
# define PWM_PRD_MASK GENMASK(15, 0)
# define PWM_DTY_MASK GENMASK(15, 0)
2017-05-30 22:32:07 +03:00
# define PWM_REG_PRD(reg) ((((reg) >> 16) & PWM_PRD_MASK) + 1)
# define PWM_REG_DTY(reg) ((reg) & PWM_DTY_MASK)
# define PWM_REG_PRESCAL(reg, chan) (((reg) >> ((chan) * PWMCH_OFFSET)) & PWM_PRESCAL_MASK)
2014-12-18 00:15:39 +03:00
# define BIT_CH(bit, chan) ((bit) << ((chan) * PWMCH_OFFSET))
static const u32 prescaler_table [ ] = {
120 ,
180 ,
240 ,
360 ,
480 ,
0 ,
0 ,
0 ,
12000 ,
24000 ,
36000 ,
48000 ,
72000 ,
0 ,
0 ,
0 , /* Actually 1 but tested separately */
} ;
struct sun4i_pwm_data {
bool has_prescaler_bypass ;
2019-11-24 20:29:07 +03:00
bool has_direct_mod_clk_output ;
2015-10-11 12:49:57 +03:00
unsigned int npwm ;
2014-12-18 00:15:39 +03:00
} ;
struct sun4i_pwm_chip {
struct pwm_chip chip ;
2019-11-24 20:29:05 +03:00
struct clk * bus_clk ;
2014-12-18 00:15:39 +03:00
struct clk * clk ;
2019-11-24 20:29:03 +03:00
struct reset_control * rst ;
2014-12-18 00:15:39 +03:00
void __iomem * base ;
spinlock_t ctrl_lock ;
const struct sun4i_pwm_data * data ;
2017-05-30 22:32:08 +03:00
unsigned long next_period [ 2 ] ;
2014-12-18 00:15:39 +03:00
} ;
static inline struct sun4i_pwm_chip * to_sun4i_pwm_chip ( struct pwm_chip * chip )
{
return container_of ( chip , struct sun4i_pwm_chip , chip ) ;
}
static inline u32 sun4i_pwm_readl ( struct sun4i_pwm_chip * chip ,
unsigned long offset )
{
return readl ( chip - > base + offset ) ;
}
static inline void sun4i_pwm_writel ( struct sun4i_pwm_chip * chip ,
u32 val , unsigned long offset )
{
writel ( val , chip - > base + offset ) ;
}
2017-05-30 22:32:07 +03:00
static void sun4i_pwm_get_state ( struct pwm_chip * chip ,
struct pwm_device * pwm ,
struct pwm_state * state )
{
struct sun4i_pwm_chip * sun4i_pwm = to_sun4i_pwm_chip ( chip ) ;
u64 clk_rate , tmp ;
u32 val ;
unsigned int prescaler ;
clk_rate = clk_get_rate ( sun4i_pwm - > clk ) ;
val = sun4i_pwm_readl ( sun4i_pwm , PWM_CTRL_REG ) ;
2019-11-24 20:29:07 +03:00
/*
* PWM chapter in H6 manual has a diagram which explains that if bypass
* bit is set , no other setting has any meaning . Even more , experiment
* proved that also enable bit is ignored in this case .
*/
if ( ( val & BIT_CH ( PWM_BYPASS , pwm - > hwpwm ) ) & &
sun4i_pwm - > data - > has_direct_mod_clk_output ) {
state - > period = DIV_ROUND_UP_ULL ( NSEC_PER_SEC , clk_rate ) ;
state - > duty_cycle = DIV_ROUND_UP_ULL ( state - > period , 2 ) ;
state - > polarity = PWM_POLARITY_NORMAL ;
state - > enabled = true ;
return ;
}
2018-02-25 04:55:58 +03:00
if ( ( PWM_REG_PRESCAL ( val , pwm - > hwpwm ) = = PWM_PRESCAL_MASK ) & &
sun4i_pwm - > data - > has_prescaler_bypass )
2017-05-30 22:32:07 +03:00
prescaler = 1 ;
else
prescaler = prescaler_table [ PWM_REG_PRESCAL ( val , pwm - > hwpwm ) ] ;
if ( prescaler = = 0 )
return ;
if ( val & BIT_CH ( PWM_ACT_STATE , pwm - > hwpwm ) )
state - > polarity = PWM_POLARITY_NORMAL ;
else
state - > polarity = PWM_POLARITY_INVERSED ;
2018-02-25 04:55:58 +03:00
if ( ( val & BIT_CH ( PWM_CLK_GATING | PWM_EN , pwm - > hwpwm ) ) = =
BIT_CH ( PWM_CLK_GATING | PWM_EN , pwm - > hwpwm ) )
2017-05-30 22:32:07 +03:00
state - > enabled = true ;
else
state - > enabled = false ;
val = sun4i_pwm_readl ( sun4i_pwm , PWM_CH_PRD ( pwm - > hwpwm ) ) ;
2019-10-14 16:53:03 +03:00
tmp = ( u64 ) prescaler * NSEC_PER_SEC * PWM_REG_DTY ( val ) ;
2017-05-30 22:32:07 +03:00
state - > duty_cycle = DIV_ROUND_CLOSEST_ULL ( tmp , clk_rate ) ;
2019-10-14 16:53:03 +03:00
tmp = ( u64 ) prescaler * NSEC_PER_SEC * PWM_REG_PRD ( val ) ;
2017-05-30 22:32:07 +03:00
state - > period = DIV_ROUND_CLOSEST_ULL ( tmp , clk_rate ) ;
}
2017-05-30 22:32:08 +03:00
static int sun4i_pwm_calculate ( struct sun4i_pwm_chip * sun4i_pwm ,
2019-08-24 18:37:07 +03:00
const struct pwm_state * state ,
2019-11-24 20:29:07 +03:00
u32 * dty , u32 * prd , unsigned int * prsclr ,
bool * bypass )
2017-05-30 22:32:08 +03:00
{
u64 clk_rate , div = 0 ;
2019-12-10 13:24:44 +03:00
unsigned int prescaler = 0 ;
2017-05-30 22:32:08 +03:00
clk_rate = clk_get_rate ( sun4i_pwm - > clk ) ;
2019-11-24 20:29:07 +03:00
* bypass = sun4i_pwm - > data - > has_direct_mod_clk_output & &
state - > enabled & &
( state - > period * clk_rate > = NSEC_PER_SEC ) & &
( state - > period * clk_rate < 2 * NSEC_PER_SEC ) & &
( state - > duty_cycle * clk_rate * 2 > = NSEC_PER_SEC ) ;
/* Skip calculation of other parameters if we bypass them */
if ( * bypass )
return 0 ;
2017-05-30 22:32:08 +03:00
if ( sun4i_pwm - > data - > has_prescaler_bypass ) {
/* First, test without any prescaler when available */
prescaler = PWM_PRESCAL_MASK ;
/*
* When not using any prescaler , the clock period in nanoseconds
* is not an integer so round it half up instead of
* truncating to get less surprising values .
*/
div = clk_rate * state - > period + NSEC_PER_SEC / 2 ;
do_div ( div , NSEC_PER_SEC ) ;
if ( div - 1 > PWM_PRD_MASK )
prescaler = 0 ;
}
if ( prescaler = = 0 ) {
/* Go up from the first divider */
for ( prescaler = 0 ; prescaler < PWM_PRESCAL_MASK ; prescaler + + ) {
2019-12-10 13:24:44 +03:00
unsigned int pval = prescaler_table [ prescaler ] ;
if ( ! pval )
2017-05-30 22:32:08 +03:00
continue ;
2019-12-10 13:24:44 +03:00
2017-05-30 22:32:08 +03:00
div = clk_rate ;
do_div ( div , pval ) ;
div = div * state - > period ;
do_div ( div , NSEC_PER_SEC ) ;
if ( div - 1 < = PWM_PRD_MASK )
break ;
}
if ( div - 1 > PWM_PRD_MASK )
return - EINVAL ;
}
* prd = div ;
div * = state - > duty_cycle ;
do_div ( div , state - > period ) ;
* dty = div ;
* prsclr = prescaler ;
return 0 ;
}
static int sun4i_pwm_apply ( struct pwm_chip * chip , struct pwm_device * pwm ,
2019-08-24 18:37:07 +03:00
const struct pwm_state * state )
2017-05-30 22:32:08 +03:00
{
struct sun4i_pwm_chip * sun4i_pwm = to_sun4i_pwm_chip ( chip ) ;
struct pwm_state cstate ;
2020-01-20 17:22:37 +03:00
u32 ctrl , duty = 0 , period = 0 , val ;
2017-05-30 22:32:08 +03:00
int ret ;
2020-01-20 17:22:37 +03:00
unsigned int delay_us , prescaler = 0 ;
2017-05-30 22:32:08 +03:00
unsigned long now ;
2019-11-24 20:29:07 +03:00
bool bypass ;
2017-05-30 22:32:08 +03:00
pwm_get_state ( pwm , & cstate ) ;
if ( ! cstate . enabled ) {
ret = clk_prepare_enable ( sun4i_pwm - > clk ) ;
if ( ret ) {
dev_err ( chip - > dev , " failed to enable PWM clock \n " ) ;
return ret ;
}
}
2019-11-24 20:29:07 +03:00
ret = sun4i_pwm_calculate ( sun4i_pwm , state , & duty , & period , & prescaler ,
& bypass ) ;
2019-11-24 20:29:06 +03:00
if ( ret ) {
dev_err ( chip - > dev , " period exceeds the maximum value \n " ) ;
if ( ! cstate . enabled )
clk_disable_unprepare ( sun4i_pwm - > clk ) ;
return ret ;
}
2017-05-30 22:32:08 +03:00
2020-01-13 12:23:13 +03:00
spin_lock ( & sun4i_pwm - > ctrl_lock ) ;
ctrl = sun4i_pwm_readl ( sun4i_pwm , PWM_CTRL_REG ) ;
2019-11-24 20:29:07 +03:00
if ( sun4i_pwm - > data - > has_direct_mod_clk_output ) {
if ( bypass ) {
ctrl | = BIT_CH ( PWM_BYPASS , pwm - > hwpwm ) ;
/* We can skip other parameter */
sun4i_pwm_writel ( sun4i_pwm , ctrl , PWM_CTRL_REG ) ;
spin_unlock ( & sun4i_pwm - > ctrl_lock ) ;
return 0 ;
}
ctrl & = ~ BIT_CH ( PWM_BYPASS , pwm - > hwpwm ) ;
}
2019-11-24 20:29:06 +03:00
if ( PWM_REG_PRESCAL ( ctrl , pwm - > hwpwm ) ! = prescaler ) {
/* Prescaler changed, the clock has to be gated */
ctrl & = ~ BIT_CH ( PWM_CLK_GATING , pwm - > hwpwm ) ;
sun4i_pwm_writel ( sun4i_pwm , ctrl , PWM_CTRL_REG ) ;
2017-05-30 22:32:08 +03:00
2019-11-24 20:29:06 +03:00
ctrl & = ~ BIT_CH ( PWM_PRESCAL_MASK , pwm - > hwpwm ) ;
ctrl | = BIT_CH ( prescaler , pwm - > hwpwm ) ;
2017-05-30 22:32:08 +03:00
}
2019-11-24 20:29:06 +03:00
val = ( duty & PWM_DTY_MASK ) | PWM_PRD ( period ) ;
sun4i_pwm_writel ( sun4i_pwm , val , PWM_CH_PRD ( pwm - > hwpwm ) ) ;
sun4i_pwm - > next_period [ pwm - > hwpwm ] = jiffies +
usecs_to_jiffies ( cstate . period / 1000 + 1 ) ;
2017-05-30 22:32:08 +03:00
if ( state - > polarity ! = PWM_POLARITY_NORMAL )
ctrl & = ~ BIT_CH ( PWM_ACT_STATE , pwm - > hwpwm ) ;
else
ctrl | = BIT_CH ( PWM_ACT_STATE , pwm - > hwpwm ) ;
ctrl | = BIT_CH ( PWM_CLK_GATING , pwm - > hwpwm ) ;
2019-11-24 20:29:06 +03:00
2017-05-30 22:32:08 +03:00
if ( state - > enabled ) {
ctrl | = BIT_CH ( PWM_EN , pwm - > hwpwm ) ;
2020-03-17 18:59:03 +03:00
} else {
2017-05-30 22:32:08 +03:00
ctrl & = ~ BIT_CH ( PWM_EN , pwm - > hwpwm ) ;
ctrl & = ~ BIT_CH ( PWM_CLK_GATING , pwm - > hwpwm ) ;
}
sun4i_pwm_writel ( sun4i_pwm , ctrl , PWM_CTRL_REG ) ;
spin_unlock ( & sun4i_pwm - > ctrl_lock ) ;
if ( state - > enabled )
return 0 ;
/* We need a full period to elapse before disabling the channel. */
now = jiffies ;
2020-03-17 18:59:03 +03:00
if ( time_before ( now , sun4i_pwm - > next_period [ pwm - > hwpwm ] ) ) {
2017-05-30 22:32:08 +03:00
delay_us = jiffies_to_usecs ( sun4i_pwm - > next_period [ pwm - > hwpwm ] -
now ) ;
if ( ( delay_us / 500 ) > MAX_UDELAY_MS )
msleep ( delay_us / 1000 + 1 ) ;
else
usleep_range ( delay_us , delay_us * 2 ) ;
}
spin_lock ( & sun4i_pwm - > ctrl_lock ) ;
ctrl = sun4i_pwm_readl ( sun4i_pwm , PWM_CTRL_REG ) ;
ctrl & = ~ BIT_CH ( PWM_CLK_GATING , pwm - > hwpwm ) ;
ctrl & = ~ BIT_CH ( PWM_EN , pwm - > hwpwm ) ;
sun4i_pwm_writel ( sun4i_pwm , ctrl , PWM_CTRL_REG ) ;
spin_unlock ( & sun4i_pwm - > ctrl_lock ) ;
clk_disable_unprepare ( sun4i_pwm - > clk ) ;
return 0 ;
}
2014-12-18 00:15:39 +03:00
static const struct pwm_ops sun4i_pwm_ops = {
2017-05-30 22:32:08 +03:00
. apply = sun4i_pwm_apply ,
2017-05-30 22:32:07 +03:00
. get_state = sun4i_pwm_get_state ,
2014-12-18 00:15:39 +03:00
. owner = THIS_MODULE ,
} ;
2018-03-19 02:28:45 +03:00
static const struct sun4i_pwm_data sun4i_pwm_dual_nobypass = {
2014-12-18 00:15:39 +03:00
. has_prescaler_bypass = false ,
2015-10-11 12:49:57 +03:00
. npwm = 2 ,
} ;
2018-03-19 02:28:45 +03:00
static const struct sun4i_pwm_data sun4i_pwm_dual_bypass = {
2015-10-11 12:49:57 +03:00
. has_prescaler_bypass = true ,
. npwm = 2 ,
} ;
2018-03-19 02:28:45 +03:00
static const struct sun4i_pwm_data sun4i_pwm_single_bypass = {
2016-08-31 11:25:20 +03:00
. has_prescaler_bypass = true ,
. npwm = 1 ,
} ;
2019-11-24 20:29:08 +03:00
static const struct sun4i_pwm_data sun50i_h6_pwm_data = {
. has_prescaler_bypass = true ,
. has_direct_mod_clk_output = true ,
. npwm = 2 ,
} ;
2014-12-18 00:15:39 +03:00
static const struct of_device_id sun4i_pwm_dt_ids [ ] = {
{
. compatible = " allwinner,sun4i-a10-pwm " ,
2018-03-19 02:28:45 +03:00
. data = & sun4i_pwm_dual_nobypass ,
2015-10-11 12:49:57 +03:00
} , {
. compatible = " allwinner,sun5i-a10s-pwm " ,
2018-03-19 02:28:45 +03:00
. data = & sun4i_pwm_dual_bypass ,
2015-10-11 12:49:57 +03:00
} , {
. compatible = " allwinner,sun5i-a13-pwm " ,
2018-03-19 02:28:45 +03:00
. data = & sun4i_pwm_single_bypass ,
2014-12-18 00:15:39 +03:00
} , {
. compatible = " allwinner,sun7i-a20-pwm " ,
2018-03-19 02:28:45 +03:00
. data = & sun4i_pwm_dual_bypass ,
2016-08-31 11:25:20 +03:00
} , {
. compatible = " allwinner,sun8i-h3-pwm " ,
2018-03-19 02:28:45 +03:00
. data = & sun4i_pwm_single_bypass ,
2019-11-24 20:29:08 +03:00
} , {
. compatible = " allwinner,sun50i-h6-pwm " ,
. data = & sun50i_h6_pwm_data ,
2014-12-18 00:15:39 +03:00
} , {
/* sentinel */
} ,
} ;
MODULE_DEVICE_TABLE ( of , sun4i_pwm_dt_ids ) ;
static int sun4i_pwm_probe ( struct platform_device * pdev )
{
struct sun4i_pwm_chip * pwm ;
struct resource * res ;
2017-05-30 22:32:07 +03:00
int ret ;
2014-12-18 00:15:39 +03:00
pwm = devm_kzalloc ( & pdev - > dev , sizeof ( * pwm ) , GFP_KERNEL ) ;
if ( ! pwm )
return - ENOMEM ;
2017-10-21 20:38:12 +03:00
pwm - > data = of_device_get_match_data ( & pdev - > dev ) ;
if ( ! pwm - > data )
return - ENODEV ;
2014-12-18 00:15:39 +03:00
res = platform_get_resource ( pdev , IORESOURCE_MEM , 0 ) ;
pwm - > base = devm_ioremap_resource ( & pdev - > dev , res ) ;
if ( IS_ERR ( pwm - > base ) )
return PTR_ERR ( pwm - > base ) ;
2019-11-24 20:29:04 +03:00
/*
* All hardware variants need a source clock that is divided and
* then feeds the counter that defines the output wave form . In the
* device tree this clock is either unnamed or called " mod " .
* Some variants ( e . g . H6 ) need another clock to access the
* hardware registers ; this is called " bus " .
* So we request " mod " first ( and ignore the corner case that a
* parent provides a " mod " clock while the right one would be the
* unnamed one of the PWM device ) and if this is not found we fall
* back to the first clock of the PWM .
*/
pwm - > clk = devm_clk_get_optional ( & pdev - > dev , " mod " ) ;
if ( IS_ERR ( pwm - > clk ) ) {
2020-01-09 10:27:35 +03:00
if ( PTR_ERR ( pwm - > clk ) ! = - EPROBE_DEFER )
2019-11-24 20:29:04 +03:00
dev_err ( & pdev - > dev , " get mod clock failed %pe \n " ,
pwm - > clk ) ;
2014-12-18 00:15:39 +03:00
return PTR_ERR ( pwm - > clk ) ;
2019-11-24 20:29:04 +03:00
}
if ( ! pwm - > clk ) {
pwm - > clk = devm_clk_get ( & pdev - > dev , NULL ) ;
if ( IS_ERR ( pwm - > clk ) ) {
2020-01-09 10:27:35 +03:00
if ( PTR_ERR ( pwm - > clk ) ! = - EPROBE_DEFER )
2019-11-24 20:29:04 +03:00
dev_err ( & pdev - > dev , " get unnamed clock failed %pe \n " ,
pwm - > clk ) ;
return PTR_ERR ( pwm - > clk ) ;
}
}
2014-12-18 00:15:39 +03:00
2019-11-24 20:29:05 +03:00
pwm - > bus_clk = devm_clk_get_optional ( & pdev - > dev , " bus " ) ;
if ( IS_ERR ( pwm - > bus_clk ) ) {
2020-01-09 10:27:35 +03:00
if ( PTR_ERR ( pwm - > bus_clk ) ! = - EPROBE_DEFER )
2019-11-24 20:29:05 +03:00
dev_err ( & pdev - > dev , " get bus clock failed %pe \n " ,
pwm - > bus_clk ) ;
return PTR_ERR ( pwm - > bus_clk ) ;
}
2019-11-24 20:29:03 +03:00
pwm - > rst = devm_reset_control_get_optional_shared ( & pdev - > dev , NULL ) ;
if ( IS_ERR ( pwm - > rst ) ) {
if ( PTR_ERR ( pwm - > rst ) ! = - EPROBE_DEFER )
dev_err ( & pdev - > dev , " get reset failed %pe \n " ,
pwm - > rst ) ;
return PTR_ERR ( pwm - > rst ) ;
}
/* Deassert reset */
ret = reset_control_deassert ( pwm - > rst ) ;
if ( ret ) {
dev_err ( & pdev - > dev , " cannot deassert reset control: %pe \n " ,
ERR_PTR ( ret ) ) ;
return ret ;
}
2019-11-24 20:29:05 +03:00
/*
* We ' re keeping the bus clock on for the sake of simplicity .
* Actually it only needs to be on for hardware register accesses .
*/
ret = clk_prepare_enable ( pwm - > bus_clk ) ;
if ( ret ) {
dev_err ( & pdev - > dev , " cannot prepare and enable bus_clk %pe \n " ,
ERR_PTR ( ret ) ) ;
goto err_bus ;
}
2014-12-18 00:15:39 +03:00
pwm - > chip . dev = & pdev - > dev ;
pwm - > chip . ops = & sun4i_pwm_ops ;
pwm - > chip . base = - 1 ;
2015-10-11 12:49:57 +03:00
pwm - > chip . npwm = pwm - > data - > npwm ;
2014-12-18 00:15:39 +03:00
pwm - > chip . of_xlate = of_pwm_xlate_with_flags ;
pwm - > chip . of_pwm_n_cells = 3 ;
spin_lock_init ( & pwm - > ctrl_lock ) ;
ret = pwmchip_add ( & pwm - > chip ) ;
if ( ret < 0 ) {
dev_err ( & pdev - > dev , " failed to add PWM chip: %d \n " , ret ) ;
2019-11-24 20:29:03 +03:00
goto err_pwm_add ;
2014-12-18 00:15:39 +03:00
}
platform_set_drvdata ( pdev , pwm ) ;
return 0 ;
2019-11-24 20:29:03 +03:00
err_pwm_add :
2019-11-24 20:29:05 +03:00
clk_disable_unprepare ( pwm - > bus_clk ) ;
err_bus :
2019-11-24 20:29:03 +03:00
reset_control_assert ( pwm - > rst ) ;
return ret ;
2014-12-18 00:15:39 +03:00
}
static int sun4i_pwm_remove ( struct platform_device * pdev )
{
struct sun4i_pwm_chip * pwm = platform_get_drvdata ( pdev ) ;
2019-11-24 20:29:03 +03:00
int ret ;
ret = pwmchip_remove ( & pwm - > chip ) ;
if ( ret )
return ret ;
2019-11-24 20:29:05 +03:00
clk_disable_unprepare ( pwm - > bus_clk ) ;
2019-11-24 20:29:03 +03:00
reset_control_assert ( pwm - > rst ) ;
2014-12-18 00:15:39 +03:00
2019-11-24 20:29:03 +03:00
return 0 ;
2014-12-18 00:15:39 +03:00
}
static struct platform_driver sun4i_pwm_driver = {
. driver = {
. name = " sun4i-pwm " ,
. of_match_table = sun4i_pwm_dt_ids ,
} ,
. probe = sun4i_pwm_probe ,
. remove = sun4i_pwm_remove ,
} ;
module_platform_driver ( sun4i_pwm_driver ) ;
MODULE_ALIAS ( " platform:sun4i-pwm " ) ;
MODULE_AUTHOR ( " Alexandre Belloni <alexandre.belloni@free-electrons.com> " ) ;
MODULE_DESCRIPTION ( " Allwinner sun4i PWM driver " ) ;
MODULE_LICENSE ( " GPL v2 " ) ;