2020-10-02 16:56:13 +03:00
// SPDX-License-Identifier: GPL-2.0
/*
2023-09-07 17:12:37 +01:00
* DesignWare PWM Controller driver ( PCI part )
2020-10-02 16:56:13 +03:00
*
* 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 .
*/
2023-09-07 17:12:37 +01:00
# define DEFAULT_MOUDLE_NAMESPACE dwc_pwm
2020-10-02 16:56:13 +03:00
# 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>
2023-09-07 17:12:37 +01:00
# include "pwm-dwc.h"
2022-12-23 15:38:14 +00:00
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 15:38:14 +00: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 15:38:13 +00: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 15:38:13 +00: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 15:38:13 +00:00
dev_err ( dev , " Base address missing \n " ) ;
2020-10-02 16:56:13 +03:00
return - ENOMEM ;
}
2022-12-23 15:38:15 +00: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 " ) ;