2020-10-02 16:56:13 +03:00
// SPDX-License-Identifier: GPL-2.0
/*
* DesignWare PWM Controller driver
*
* Copyright ( C ) 2018 - 2020 Intel Corporation
*
* Author : Felipe Balbi ( Intel )
* Author : Jarkko Nikula < jarkko . nikula @ linux . intel . com >
* Author : Raymond Tan < raymond . tan @ intel . com >
*
* Limitations :
* - The hardware cannot generate a 0 % or 100 % duty cycle . Both high and low
* periods are one or more input clock periods long .
*/
# include <linux/bitops.h>
# include <linux/export.h>
# include <linux/kernel.h>
# include <linux/module.h>
# include <linux/pci.h>
# include <linux/pm_runtime.h>
# include <linux/pwm.h>
# define DWC_TIM_LD_CNT(n) ((n) * 0x14)
# define DWC_TIM_LD_CNT2(n) (((n) * 4) + 0xb0)
# define DWC_TIM_CUR_VAL(n) (((n) * 0x14) + 0x04)
# define DWC_TIM_CTRL(n) (((n) * 0x14) + 0x08)
# define DWC_TIM_EOI(n) (((n) * 0x14) + 0x0c)
# define DWC_TIM_INT_STS(n) (((n) * 0x14) + 0x10)
# define DWC_TIMERS_INT_STS 0xa0
# define DWC_TIMERS_EOI 0xa4
# define DWC_TIMERS_RAW_INT_STS 0xa8
# define DWC_TIMERS_COMP_VERSION 0xac
# define DWC_TIMERS_TOTAL 8
# define DWC_CLK_PERIOD_NS 10
/* Timer Control Register */
# define DWC_TIM_CTRL_EN BIT(0)
# define DWC_TIM_CTRL_MODE BIT(1)
# define DWC_TIM_CTRL_MODE_FREE (0 << 1)
# define DWC_TIM_CTRL_MODE_USER (1 << 1)
# define DWC_TIM_CTRL_INT_MASK BIT(2)
# define DWC_TIM_CTRL_PWM BIT(3)
struct dwc_pwm_ctx {
u32 cnt ;
u32 cnt2 ;
u32 ctrl ;
} ;
struct dwc_pwm {
struct pwm_chip chip ;
void __iomem * base ;
struct dwc_pwm_ctx ctx [ DWC_TIMERS_TOTAL ] ;
} ;
# define to_dwc_pwm(p) (container_of((p), struct dwc_pwm, chip))
static inline u32 dwc_pwm_readl ( struct dwc_pwm * dwc , u32 offset )
{
return readl ( dwc - > base + offset ) ;
}
static inline void dwc_pwm_writel ( struct dwc_pwm * dwc , u32 value , u32 offset )
{
writel ( value , dwc - > base + offset ) ;
}
static void __dwc_pwm_set_enable ( struct dwc_pwm * dwc , int pwm , int enabled )
{
u32 reg ;
reg = dwc_pwm_readl ( dwc , DWC_TIM_CTRL ( pwm ) ) ;
if ( enabled )
reg | = DWC_TIM_CTRL_EN ;
else
reg & = ~ DWC_TIM_CTRL_EN ;
dwc_pwm_writel ( dwc , reg , DWC_TIM_CTRL ( pwm ) ) ;
}
static int __dwc_pwm_configure_timer ( struct dwc_pwm * dwc ,
struct pwm_device * pwm ,
const struct pwm_state * state )
{
u64 tmp ;
u32 ctrl ;
u32 high ;
u32 low ;
/*
* Calculate width of low and high period in terms of input clock
* periods and check are the result within HW limits between 1 and
* 2 ^ 32 periods .
*/
tmp = DIV_ROUND_CLOSEST_ULL ( state - > duty_cycle , DWC_CLK_PERIOD_NS ) ;
if ( tmp < 1 | | tmp > ( 1ULL < < 32 ) )
return - ERANGE ;
low = tmp - 1 ;
tmp = DIV_ROUND_CLOSEST_ULL ( state - > period - state - > duty_cycle ,
DWC_CLK_PERIOD_NS ) ;
if ( tmp < 1 | | tmp > ( 1ULL < < 32 ) )
return - ERANGE ;
high = tmp - 1 ;
/*
* Specification says timer usage flow is to disable timer , then
* program it followed by enable . It also says Load Count is loaded
* into timer after it is enabled - either after a disable or
* a reset . Based on measurements it happens also without disable
* whenever Load Count is updated . But follow the specification .
*/
__dwc_pwm_set_enable ( dwc , pwm - > hwpwm , false ) ;
/*
* Write Load Count and Load Count 2 registers . Former defines the
* width of low period and latter the width of high period in terms
* multiple of input clock periods :
* Width = ( ( Count + 1 ) * input clock period ) .
*/
dwc_pwm_writel ( dwc , low , DWC_TIM_LD_CNT ( pwm - > hwpwm ) ) ;
dwc_pwm_writel ( dwc , high , DWC_TIM_LD_CNT2 ( pwm - > hwpwm ) ) ;
/*
* Set user - defined mode , timer reloads from Load Count registers
* when it counts down to 0.
* Set PWM mode , it makes output to toggle and width of low and high
* periods are set by Load Count registers .
*/
ctrl = DWC_TIM_CTRL_MODE_USER | DWC_TIM_CTRL_PWM ;
dwc_pwm_writel ( dwc , ctrl , DWC_TIM_CTRL ( pwm - > hwpwm ) ) ;
/*
* Enable timer . Output starts from low period .
*/
__dwc_pwm_set_enable ( dwc , pwm - > hwpwm , state - > enabled ) ;
return 0 ;
}
static int dwc_pwm_apply ( struct pwm_chip * chip , struct pwm_device * pwm ,
const struct pwm_state * state )
{
struct dwc_pwm * dwc = to_dwc_pwm ( chip ) ;
if ( state - > polarity ! = PWM_POLARITY_INVERSED )
return - EINVAL ;
if ( state - > enabled ) {
if ( ! pwm - > state . enabled )
pm_runtime_get_sync ( chip - > dev ) ;
return __dwc_pwm_configure_timer ( dwc , pwm , state ) ;
} else {
if ( pwm - > state . enabled ) {
__dwc_pwm_set_enable ( dwc , pwm - > hwpwm , false ) ;
pm_runtime_put_sync ( chip - > dev ) ;
}
}
return 0 ;
}
2022-12-02 21:35:26 +03:00
static int dwc_pwm_get_state ( struct pwm_chip * chip , struct pwm_device * pwm ,
struct pwm_state * state )
2020-10-02 16:56:13 +03:00
{
struct dwc_pwm * dwc = to_dwc_pwm ( chip ) ;
u64 duty , period ;
pm_runtime_get_sync ( chip - > dev ) ;
state - > enabled = ! ! ( dwc_pwm_readl ( dwc ,
DWC_TIM_CTRL ( pwm - > hwpwm ) ) & DWC_TIM_CTRL_EN ) ;
duty = dwc_pwm_readl ( dwc , DWC_TIM_LD_CNT ( pwm - > hwpwm ) ) ;
duty + = 1 ;
duty * = DWC_CLK_PERIOD_NS ;
state - > duty_cycle = duty ;
period = dwc_pwm_readl ( dwc , DWC_TIM_LD_CNT2 ( pwm - > hwpwm ) ) ;
period + = 1 ;
period * = DWC_CLK_PERIOD_NS ;
period + = duty ;
state - > period = period ;
state - > polarity = PWM_POLARITY_INVERSED ;
pm_runtime_put_sync ( chip - > dev ) ;
2022-12-02 21:35:26 +03:00
return 0 ;
2020-10-02 16:56:13 +03:00
}
static const struct pwm_ops dwc_pwm_ops = {
. apply = dwc_pwm_apply ,
. get_state = dwc_pwm_get_state ,
. owner = THIS_MODULE ,
} ;
2022-12-23 18:38:14 +03:00
static struct dwc_pwm * dwc_pwm_alloc ( struct device * dev )
{
struct dwc_pwm * dwc ;
dwc = devm_kzalloc ( dev , sizeof ( * dwc ) , GFP_KERNEL ) ;
if ( ! dwc )
return NULL ;
dwc - > chip . dev = dev ;
dwc - > chip . ops = & dwc_pwm_ops ;
dwc - > chip . npwm = DWC_TIMERS_TOTAL ;
dev_set_drvdata ( dev , dwc ) ;
return dwc ;
}
2020-10-02 16:56:13 +03:00
static int dwc_pwm_probe ( struct pci_dev * pci , const struct pci_device_id * id )
{
struct device * dev = & pci - > dev ;
struct dwc_pwm * dwc ;
int ret ;
2022-12-23 18:38:14 +03:00
dwc = dwc_pwm_alloc ( dev ) ;
2020-10-02 16:56:13 +03:00
if ( ! dwc )
return - ENOMEM ;
ret = pcim_enable_device ( pci ) ;
if ( ret ) {
2022-12-23 18:38:13 +03:00
dev_err ( dev , " Failed to enable device (%pe) \n " , ERR_PTR ( ret ) ) ;
2020-10-02 16:56:13 +03:00
return ret ;
}
pci_set_master ( pci ) ;
ret = pcim_iomap_regions ( pci , BIT ( 0 ) , pci_name ( pci ) ) ;
if ( ret ) {
2022-12-23 18:38:13 +03:00
dev_err ( dev , " Failed to iomap PCI BAR (%pe) \n " , ERR_PTR ( ret ) ) ;
2020-10-02 16:56:13 +03:00
return ret ;
}
dwc - > base = pcim_iomap_table ( pci ) [ 0 ] ;
if ( ! dwc - > base ) {
2022-12-23 18:38:13 +03:00
dev_err ( dev , " Base address missing \n " ) ;
2020-10-02 16:56:13 +03:00
return - ENOMEM ;
}
2022-12-23 18:38:15 +03:00
ret = devm_pwmchip_add ( dev , & dwc - > chip ) ;
2020-10-02 16:56:13 +03:00
if ( ret )
return ret ;
pm_runtime_put ( dev ) ;
pm_runtime_allow ( dev ) ;
return 0 ;
}
static void dwc_pwm_remove ( struct pci_dev * pci )
{
pm_runtime_forbid ( & pci - > dev ) ;
pm_runtime_get_noresume ( & pci - > dev ) ;
}
# ifdef CONFIG_PM_SLEEP
static int dwc_pwm_suspend ( struct device * dev )
{
struct pci_dev * pdev = container_of ( dev , struct pci_dev , dev ) ;
struct dwc_pwm * dwc = pci_get_drvdata ( pdev ) ;
int i ;
for ( i = 0 ; i < DWC_TIMERS_TOTAL ; i + + ) {
if ( dwc - > chip . pwms [ i ] . state . enabled ) {
dev_err ( dev , " PWM %u in use by consumer (%s) \n " ,
i , dwc - > chip . pwms [ i ] . label ) ;
return - EBUSY ;
}
dwc - > ctx [ i ] . cnt = dwc_pwm_readl ( dwc , DWC_TIM_LD_CNT ( i ) ) ;
dwc - > ctx [ i ] . cnt2 = dwc_pwm_readl ( dwc , DWC_TIM_LD_CNT2 ( i ) ) ;
dwc - > ctx [ i ] . ctrl = dwc_pwm_readl ( dwc , DWC_TIM_CTRL ( i ) ) ;
}
return 0 ;
}
static int dwc_pwm_resume ( struct device * dev )
{
struct pci_dev * pdev = container_of ( dev , struct pci_dev , dev ) ;
struct dwc_pwm * dwc = pci_get_drvdata ( pdev ) ;
int i ;
for ( i = 0 ; i < DWC_TIMERS_TOTAL ; i + + ) {
dwc_pwm_writel ( dwc , dwc - > ctx [ i ] . cnt , DWC_TIM_LD_CNT ( i ) ) ;
dwc_pwm_writel ( dwc , dwc - > ctx [ i ] . cnt2 , DWC_TIM_LD_CNT2 ( i ) ) ;
dwc_pwm_writel ( dwc , dwc - > ctx [ i ] . ctrl , DWC_TIM_CTRL ( i ) ) ;
}
return 0 ;
}
# endif
static SIMPLE_DEV_PM_OPS ( dwc_pwm_pm_ops , dwc_pwm_suspend , dwc_pwm_resume ) ;
static const struct pci_device_id dwc_pwm_id_table [ ] = {
{ PCI_VDEVICE ( INTEL , 0x4bb7 ) } , /* Elkhart Lake */
{ } /* Terminating Entry */
} ;
MODULE_DEVICE_TABLE ( pci , dwc_pwm_id_table ) ;
static struct pci_driver dwc_pwm_driver = {
. name = " pwm-dwc " ,
. probe = dwc_pwm_probe ,
. remove = dwc_pwm_remove ,
. id_table = dwc_pwm_id_table ,
. driver = {
. pm = & dwc_pwm_pm_ops ,
} ,
} ;
module_pci_driver ( dwc_pwm_driver ) ;
MODULE_AUTHOR ( " Felipe Balbi (Intel) " ) ;
MODULE_AUTHOR ( " Jarkko Nikula <jarkko.nikula@linux.intel.com> " ) ;
MODULE_AUTHOR ( " Raymond Tan <raymond.tan@intel.com> " ) ;
MODULE_DESCRIPTION ( " DesignWare PWM Controller " ) ;
MODULE_LICENSE ( " GPL " ) ;