xref: /openbmc/linux/drivers/pwm/pwm-dwc.c (revision 9a87ffc99ec8eb8d35eed7c4f816d75f5cc9662e)
11ed2b3fcSJarkko Nikula // SPDX-License-Identifier: GPL-2.0
21ed2b3fcSJarkko Nikula /*
31ed2b3fcSJarkko Nikula  * DesignWare PWM Controller driver
41ed2b3fcSJarkko Nikula  *
51ed2b3fcSJarkko Nikula  * Copyright (C) 2018-2020 Intel Corporation
61ed2b3fcSJarkko Nikula  *
71ed2b3fcSJarkko Nikula  * Author: Felipe Balbi (Intel)
81ed2b3fcSJarkko Nikula  * Author: Jarkko Nikula <jarkko.nikula@linux.intel.com>
91ed2b3fcSJarkko Nikula  * Author: Raymond Tan <raymond.tan@intel.com>
101ed2b3fcSJarkko Nikula  *
111ed2b3fcSJarkko Nikula  * Limitations:
121ed2b3fcSJarkko Nikula  * - The hardware cannot generate a 0 % or 100 % duty cycle. Both high and low
131ed2b3fcSJarkko Nikula  *   periods are one or more input clock periods long.
141ed2b3fcSJarkko Nikula  */
151ed2b3fcSJarkko Nikula 
161ed2b3fcSJarkko Nikula #include <linux/bitops.h>
171ed2b3fcSJarkko Nikula #include <linux/export.h>
181ed2b3fcSJarkko Nikula #include <linux/kernel.h>
191ed2b3fcSJarkko Nikula #include <linux/module.h>
201ed2b3fcSJarkko Nikula #include <linux/pci.h>
211ed2b3fcSJarkko Nikula #include <linux/pm_runtime.h>
221ed2b3fcSJarkko Nikula #include <linux/pwm.h>
231ed2b3fcSJarkko Nikula 
241ed2b3fcSJarkko Nikula #define DWC_TIM_LD_CNT(n)	((n) * 0x14)
251ed2b3fcSJarkko Nikula #define DWC_TIM_LD_CNT2(n)	(((n) * 4) + 0xb0)
261ed2b3fcSJarkko Nikula #define DWC_TIM_CUR_VAL(n)	(((n) * 0x14) + 0x04)
271ed2b3fcSJarkko Nikula #define DWC_TIM_CTRL(n)		(((n) * 0x14) + 0x08)
281ed2b3fcSJarkko Nikula #define DWC_TIM_EOI(n)		(((n) * 0x14) + 0x0c)
291ed2b3fcSJarkko Nikula #define DWC_TIM_INT_STS(n)	(((n) * 0x14) + 0x10)
301ed2b3fcSJarkko Nikula 
311ed2b3fcSJarkko Nikula #define DWC_TIMERS_INT_STS	0xa0
321ed2b3fcSJarkko Nikula #define DWC_TIMERS_EOI		0xa4
331ed2b3fcSJarkko Nikula #define DWC_TIMERS_RAW_INT_STS	0xa8
341ed2b3fcSJarkko Nikula #define DWC_TIMERS_COMP_VERSION	0xac
351ed2b3fcSJarkko Nikula 
361ed2b3fcSJarkko Nikula #define DWC_TIMERS_TOTAL	8
371ed2b3fcSJarkko Nikula #define DWC_CLK_PERIOD_NS	10
381ed2b3fcSJarkko Nikula 
391ed2b3fcSJarkko Nikula /* Timer Control Register */
401ed2b3fcSJarkko Nikula #define DWC_TIM_CTRL_EN		BIT(0)
411ed2b3fcSJarkko Nikula #define DWC_TIM_CTRL_MODE	BIT(1)
421ed2b3fcSJarkko Nikula #define DWC_TIM_CTRL_MODE_FREE	(0 << 1)
431ed2b3fcSJarkko Nikula #define DWC_TIM_CTRL_MODE_USER	(1 << 1)
441ed2b3fcSJarkko Nikula #define DWC_TIM_CTRL_INT_MASK	BIT(2)
451ed2b3fcSJarkko Nikula #define DWC_TIM_CTRL_PWM	BIT(3)
461ed2b3fcSJarkko Nikula 
471ed2b3fcSJarkko Nikula struct dwc_pwm_ctx {
481ed2b3fcSJarkko Nikula 	u32 cnt;
491ed2b3fcSJarkko Nikula 	u32 cnt2;
501ed2b3fcSJarkko Nikula 	u32 ctrl;
511ed2b3fcSJarkko Nikula };
521ed2b3fcSJarkko Nikula 
531ed2b3fcSJarkko Nikula struct dwc_pwm {
541ed2b3fcSJarkko Nikula 	struct pwm_chip chip;
551ed2b3fcSJarkko Nikula 	void __iomem *base;
561ed2b3fcSJarkko Nikula 	struct dwc_pwm_ctx ctx[DWC_TIMERS_TOTAL];
571ed2b3fcSJarkko Nikula };
581ed2b3fcSJarkko Nikula #define to_dwc_pwm(p)	(container_of((p), struct dwc_pwm, chip))
591ed2b3fcSJarkko Nikula 
dwc_pwm_readl(struct dwc_pwm * dwc,u32 offset)601ed2b3fcSJarkko Nikula static inline u32 dwc_pwm_readl(struct dwc_pwm *dwc, u32 offset)
611ed2b3fcSJarkko Nikula {
621ed2b3fcSJarkko Nikula 	return readl(dwc->base + offset);
631ed2b3fcSJarkko Nikula }
641ed2b3fcSJarkko Nikula 
dwc_pwm_writel(struct dwc_pwm * dwc,u32 value,u32 offset)651ed2b3fcSJarkko Nikula static inline void dwc_pwm_writel(struct dwc_pwm *dwc, u32 value, u32 offset)
661ed2b3fcSJarkko Nikula {
671ed2b3fcSJarkko Nikula 	writel(value, dwc->base + offset);
681ed2b3fcSJarkko Nikula }
691ed2b3fcSJarkko Nikula 
__dwc_pwm_set_enable(struct dwc_pwm * dwc,int pwm,int enabled)701ed2b3fcSJarkko Nikula static void __dwc_pwm_set_enable(struct dwc_pwm *dwc, int pwm, int enabled)
711ed2b3fcSJarkko Nikula {
721ed2b3fcSJarkko Nikula 	u32 reg;
731ed2b3fcSJarkko Nikula 
741ed2b3fcSJarkko Nikula 	reg = dwc_pwm_readl(dwc, DWC_TIM_CTRL(pwm));
751ed2b3fcSJarkko Nikula 
761ed2b3fcSJarkko Nikula 	if (enabled)
771ed2b3fcSJarkko Nikula 		reg |= DWC_TIM_CTRL_EN;
781ed2b3fcSJarkko Nikula 	else
791ed2b3fcSJarkko Nikula 		reg &= ~DWC_TIM_CTRL_EN;
801ed2b3fcSJarkko Nikula 
811ed2b3fcSJarkko Nikula 	dwc_pwm_writel(dwc, reg, DWC_TIM_CTRL(pwm));
821ed2b3fcSJarkko Nikula }
831ed2b3fcSJarkko Nikula 
__dwc_pwm_configure_timer(struct dwc_pwm * dwc,struct pwm_device * pwm,const struct pwm_state * state)841ed2b3fcSJarkko Nikula static int __dwc_pwm_configure_timer(struct dwc_pwm *dwc,
851ed2b3fcSJarkko Nikula 				     struct pwm_device *pwm,
861ed2b3fcSJarkko Nikula 				     const struct pwm_state *state)
871ed2b3fcSJarkko Nikula {
881ed2b3fcSJarkko Nikula 	u64 tmp;
891ed2b3fcSJarkko Nikula 	u32 ctrl;
901ed2b3fcSJarkko Nikula 	u32 high;
911ed2b3fcSJarkko Nikula 	u32 low;
921ed2b3fcSJarkko Nikula 
931ed2b3fcSJarkko Nikula 	/*
941ed2b3fcSJarkko Nikula 	 * Calculate width of low and high period in terms of input clock
951ed2b3fcSJarkko Nikula 	 * periods and check are the result within HW limits between 1 and
961ed2b3fcSJarkko Nikula 	 * 2^32 periods.
971ed2b3fcSJarkko Nikula 	 */
981ed2b3fcSJarkko Nikula 	tmp = DIV_ROUND_CLOSEST_ULL(state->duty_cycle, DWC_CLK_PERIOD_NS);
991ed2b3fcSJarkko Nikula 	if (tmp < 1 || tmp > (1ULL << 32))
1001ed2b3fcSJarkko Nikula 		return -ERANGE;
1011ed2b3fcSJarkko Nikula 	low = tmp - 1;
1021ed2b3fcSJarkko Nikula 
1031ed2b3fcSJarkko Nikula 	tmp = DIV_ROUND_CLOSEST_ULL(state->period - state->duty_cycle,
1041ed2b3fcSJarkko Nikula 				    DWC_CLK_PERIOD_NS);
1051ed2b3fcSJarkko Nikula 	if (tmp < 1 || tmp > (1ULL << 32))
1061ed2b3fcSJarkko Nikula 		return -ERANGE;
1071ed2b3fcSJarkko Nikula 	high = tmp - 1;
1081ed2b3fcSJarkko Nikula 
1091ed2b3fcSJarkko Nikula 	/*
1101ed2b3fcSJarkko Nikula 	 * Specification says timer usage flow is to disable timer, then
1111ed2b3fcSJarkko Nikula 	 * program it followed by enable. It also says Load Count is loaded
1121ed2b3fcSJarkko Nikula 	 * into timer after it is enabled - either after a disable or
1131ed2b3fcSJarkko Nikula 	 * a reset. Based on measurements it happens also without disable
1141ed2b3fcSJarkko Nikula 	 * whenever Load Count is updated. But follow the specification.
1151ed2b3fcSJarkko Nikula 	 */
1161ed2b3fcSJarkko Nikula 	__dwc_pwm_set_enable(dwc, pwm->hwpwm, false);
1171ed2b3fcSJarkko Nikula 
1181ed2b3fcSJarkko Nikula 	/*
1191ed2b3fcSJarkko Nikula 	 * Write Load Count and Load Count 2 registers. Former defines the
1201ed2b3fcSJarkko Nikula 	 * width of low period and latter the width of high period in terms
1211ed2b3fcSJarkko Nikula 	 * multiple of input clock periods:
1221ed2b3fcSJarkko Nikula 	 * Width = ((Count + 1) * input clock period).
1231ed2b3fcSJarkko Nikula 	 */
1241ed2b3fcSJarkko Nikula 	dwc_pwm_writel(dwc, low, DWC_TIM_LD_CNT(pwm->hwpwm));
1251ed2b3fcSJarkko Nikula 	dwc_pwm_writel(dwc, high, DWC_TIM_LD_CNT2(pwm->hwpwm));
1261ed2b3fcSJarkko Nikula 
1271ed2b3fcSJarkko Nikula 	/*
1281ed2b3fcSJarkko Nikula 	 * Set user-defined mode, timer reloads from Load Count registers
1291ed2b3fcSJarkko Nikula 	 * when it counts down to 0.
1301ed2b3fcSJarkko Nikula 	 * Set PWM mode, it makes output to toggle and width of low and high
1311ed2b3fcSJarkko Nikula 	 * periods are set by Load Count registers.
1321ed2b3fcSJarkko Nikula 	 */
1331ed2b3fcSJarkko Nikula 	ctrl = DWC_TIM_CTRL_MODE_USER | DWC_TIM_CTRL_PWM;
1341ed2b3fcSJarkko Nikula 	dwc_pwm_writel(dwc, ctrl, DWC_TIM_CTRL(pwm->hwpwm));
1351ed2b3fcSJarkko Nikula 
1361ed2b3fcSJarkko Nikula 	/*
1371ed2b3fcSJarkko Nikula 	 * Enable timer. Output starts from low period.
1381ed2b3fcSJarkko Nikula 	 */
1391ed2b3fcSJarkko Nikula 	__dwc_pwm_set_enable(dwc, pwm->hwpwm, state->enabled);
1401ed2b3fcSJarkko Nikula 
1411ed2b3fcSJarkko Nikula 	return 0;
1421ed2b3fcSJarkko Nikula }
1431ed2b3fcSJarkko Nikula 
dwc_pwm_apply(struct pwm_chip * chip,struct pwm_device * pwm,const struct pwm_state * state)1441ed2b3fcSJarkko Nikula static int dwc_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
1451ed2b3fcSJarkko Nikula 			 const struct pwm_state *state)
1461ed2b3fcSJarkko Nikula {
1471ed2b3fcSJarkko Nikula 	struct dwc_pwm *dwc = to_dwc_pwm(chip);
1481ed2b3fcSJarkko Nikula 
1491ed2b3fcSJarkko Nikula 	if (state->polarity != PWM_POLARITY_INVERSED)
1501ed2b3fcSJarkko Nikula 		return -EINVAL;
1511ed2b3fcSJarkko Nikula 
1521ed2b3fcSJarkko Nikula 	if (state->enabled) {
1531ed2b3fcSJarkko Nikula 		if (!pwm->state.enabled)
1541ed2b3fcSJarkko Nikula 			pm_runtime_get_sync(chip->dev);
1551ed2b3fcSJarkko Nikula 		return __dwc_pwm_configure_timer(dwc, pwm, state);
1561ed2b3fcSJarkko Nikula 	} else {
1571ed2b3fcSJarkko Nikula 		if (pwm->state.enabled) {
1581ed2b3fcSJarkko Nikula 			__dwc_pwm_set_enable(dwc, pwm->hwpwm, false);
1591ed2b3fcSJarkko Nikula 			pm_runtime_put_sync(chip->dev);
1601ed2b3fcSJarkko Nikula 		}
1611ed2b3fcSJarkko Nikula 	}
1621ed2b3fcSJarkko Nikula 
1631ed2b3fcSJarkko Nikula 	return 0;
1641ed2b3fcSJarkko Nikula }
1651ed2b3fcSJarkko Nikula 
dwc_pwm_get_state(struct pwm_chip * chip,struct pwm_device * pwm,struct pwm_state * state)1666c452cffSUwe Kleine-König static int dwc_pwm_get_state(struct pwm_chip *chip, struct pwm_device *pwm,
1671ed2b3fcSJarkko Nikula 			     struct pwm_state *state)
1681ed2b3fcSJarkko Nikula {
1691ed2b3fcSJarkko Nikula 	struct dwc_pwm *dwc = to_dwc_pwm(chip);
1701ed2b3fcSJarkko Nikula 	u64 duty, period;
1711ed2b3fcSJarkko Nikula 
1721ed2b3fcSJarkko Nikula 	pm_runtime_get_sync(chip->dev);
1731ed2b3fcSJarkko Nikula 
1741ed2b3fcSJarkko Nikula 	state->enabled = !!(dwc_pwm_readl(dwc,
1751ed2b3fcSJarkko Nikula 				DWC_TIM_CTRL(pwm->hwpwm)) & DWC_TIM_CTRL_EN);
1761ed2b3fcSJarkko Nikula 
1771ed2b3fcSJarkko Nikula 	duty = dwc_pwm_readl(dwc, DWC_TIM_LD_CNT(pwm->hwpwm));
1781ed2b3fcSJarkko Nikula 	duty += 1;
1791ed2b3fcSJarkko Nikula 	duty *= DWC_CLK_PERIOD_NS;
1801ed2b3fcSJarkko Nikula 	state->duty_cycle = duty;
1811ed2b3fcSJarkko Nikula 
1821ed2b3fcSJarkko Nikula 	period = dwc_pwm_readl(dwc, DWC_TIM_LD_CNT2(pwm->hwpwm));
1831ed2b3fcSJarkko Nikula 	period += 1;
1841ed2b3fcSJarkko Nikula 	period *= DWC_CLK_PERIOD_NS;
1851ed2b3fcSJarkko Nikula 	period += duty;
1861ed2b3fcSJarkko Nikula 	state->period = period;
1871ed2b3fcSJarkko Nikula 
1881ed2b3fcSJarkko Nikula 	state->polarity = PWM_POLARITY_INVERSED;
1891ed2b3fcSJarkko Nikula 
1901ed2b3fcSJarkko Nikula 	pm_runtime_put_sync(chip->dev);
1916c452cffSUwe Kleine-König 
1926c452cffSUwe Kleine-König 	return 0;
1931ed2b3fcSJarkko Nikula }
1941ed2b3fcSJarkko Nikula 
1951ed2b3fcSJarkko Nikula static const struct pwm_ops dwc_pwm_ops = {
1961ed2b3fcSJarkko Nikula 	.apply = dwc_pwm_apply,
1971ed2b3fcSJarkko Nikula 	.get_state = dwc_pwm_get_state,
1981ed2b3fcSJarkko Nikula 	.owner = THIS_MODULE,
1991ed2b3fcSJarkko Nikula };
2001ed2b3fcSJarkko Nikula 
dwc_pwm_alloc(struct device * dev)201a357d149SBen Dooks static struct dwc_pwm *dwc_pwm_alloc(struct device *dev)
202a357d149SBen Dooks {
203a357d149SBen Dooks 	struct dwc_pwm *dwc;
204a357d149SBen Dooks 
205a357d149SBen Dooks 	dwc = devm_kzalloc(dev, sizeof(*dwc), GFP_KERNEL);
206a357d149SBen Dooks 	if (!dwc)
207a357d149SBen Dooks 		return NULL;
208a357d149SBen Dooks 
209a357d149SBen Dooks 	dwc->chip.dev = dev;
210a357d149SBen Dooks 	dwc->chip.ops = &dwc_pwm_ops;
211a357d149SBen Dooks 	dwc->chip.npwm = DWC_TIMERS_TOTAL;
212a357d149SBen Dooks 
213a357d149SBen Dooks 	dev_set_drvdata(dev, dwc);
214a357d149SBen Dooks 	return dwc;
215a357d149SBen Dooks }
216a357d149SBen Dooks 
dwc_pwm_probe(struct pci_dev * pci,const struct pci_device_id * id)2171ed2b3fcSJarkko Nikula static int dwc_pwm_probe(struct pci_dev *pci, const struct pci_device_id *id)
2181ed2b3fcSJarkko Nikula {
2191ed2b3fcSJarkko Nikula 	struct device *dev = &pci->dev;
2201ed2b3fcSJarkko Nikula 	struct dwc_pwm *dwc;
2211ed2b3fcSJarkko Nikula 	int ret;
2221ed2b3fcSJarkko Nikula 
223a357d149SBen Dooks 	dwc = dwc_pwm_alloc(dev);
2241ed2b3fcSJarkko Nikula 	if (!dwc)
2251ed2b3fcSJarkko Nikula 		return -ENOMEM;
2261ed2b3fcSJarkko Nikula 
2271ed2b3fcSJarkko Nikula 	ret = pcim_enable_device(pci);
2281ed2b3fcSJarkko Nikula 	if (ret) {
229f7c843d6SBen Dooks 		dev_err(dev, "Failed to enable device (%pe)\n", ERR_PTR(ret));
2301ed2b3fcSJarkko Nikula 		return ret;
2311ed2b3fcSJarkko Nikula 	}
2321ed2b3fcSJarkko Nikula 
2331ed2b3fcSJarkko Nikula 	pci_set_master(pci);
2341ed2b3fcSJarkko Nikula 
2351ed2b3fcSJarkko Nikula 	ret = pcim_iomap_regions(pci, BIT(0), pci_name(pci));
2361ed2b3fcSJarkko Nikula 	if (ret) {
237f7c843d6SBen Dooks 		dev_err(dev, "Failed to iomap PCI BAR (%pe)\n", ERR_PTR(ret));
2381ed2b3fcSJarkko Nikula 		return ret;
2391ed2b3fcSJarkko Nikula 	}
2401ed2b3fcSJarkko Nikula 
2411ed2b3fcSJarkko Nikula 	dwc->base = pcim_iomap_table(pci)[0];
2421ed2b3fcSJarkko Nikula 	if (!dwc->base) {
243f7c843d6SBen Dooks 		dev_err(dev, "Base address missing\n");
2441ed2b3fcSJarkko Nikula 		return -ENOMEM;
2451ed2b3fcSJarkko Nikula 	}
2461ed2b3fcSJarkko Nikula 
247*cf70d01aSBen Dooks 	ret = devm_pwmchip_add(dev, &dwc->chip);
2481ed2b3fcSJarkko Nikula 	if (ret)
2491ed2b3fcSJarkko Nikula 		return ret;
2501ed2b3fcSJarkko Nikula 
2511ed2b3fcSJarkko Nikula 	pm_runtime_put(dev);
2521ed2b3fcSJarkko Nikula 	pm_runtime_allow(dev);
2531ed2b3fcSJarkko Nikula 
2541ed2b3fcSJarkko Nikula 	return 0;
2551ed2b3fcSJarkko Nikula }
2561ed2b3fcSJarkko Nikula 
dwc_pwm_remove(struct pci_dev * pci)2571ed2b3fcSJarkko Nikula static void dwc_pwm_remove(struct pci_dev *pci)
2581ed2b3fcSJarkko Nikula {
2591ed2b3fcSJarkko Nikula 	pm_runtime_forbid(&pci->dev);
2601ed2b3fcSJarkko Nikula 	pm_runtime_get_noresume(&pci->dev);
2611ed2b3fcSJarkko Nikula }
2621ed2b3fcSJarkko Nikula 
2631ed2b3fcSJarkko Nikula #ifdef CONFIG_PM_SLEEP
dwc_pwm_suspend(struct device * dev)2641ed2b3fcSJarkko Nikula static int dwc_pwm_suspend(struct device *dev)
2651ed2b3fcSJarkko Nikula {
2661ed2b3fcSJarkko Nikula 	struct pci_dev *pdev = container_of(dev, struct pci_dev, dev);
2671ed2b3fcSJarkko Nikula 	struct dwc_pwm *dwc = pci_get_drvdata(pdev);
2681ed2b3fcSJarkko Nikula 	int i;
2691ed2b3fcSJarkko Nikula 
2701ed2b3fcSJarkko Nikula 	for (i = 0; i < DWC_TIMERS_TOTAL; i++) {
2711ed2b3fcSJarkko Nikula 		if (dwc->chip.pwms[i].state.enabled) {
2721ed2b3fcSJarkko Nikula 			dev_err(dev, "PWM %u in use by consumer (%s)\n",
2731ed2b3fcSJarkko Nikula 				i, dwc->chip.pwms[i].label);
2741ed2b3fcSJarkko Nikula 			return -EBUSY;
2751ed2b3fcSJarkko Nikula 		}
2761ed2b3fcSJarkko Nikula 		dwc->ctx[i].cnt = dwc_pwm_readl(dwc, DWC_TIM_LD_CNT(i));
2771ed2b3fcSJarkko Nikula 		dwc->ctx[i].cnt2 = dwc_pwm_readl(dwc, DWC_TIM_LD_CNT2(i));
2781ed2b3fcSJarkko Nikula 		dwc->ctx[i].ctrl = dwc_pwm_readl(dwc, DWC_TIM_CTRL(i));
2791ed2b3fcSJarkko Nikula 	}
2801ed2b3fcSJarkko Nikula 
2811ed2b3fcSJarkko Nikula 	return 0;
2821ed2b3fcSJarkko Nikula }
2831ed2b3fcSJarkko Nikula 
dwc_pwm_resume(struct device * dev)2841ed2b3fcSJarkko Nikula static int dwc_pwm_resume(struct device *dev)
2851ed2b3fcSJarkko Nikula {
2861ed2b3fcSJarkko Nikula 	struct pci_dev *pdev = container_of(dev, struct pci_dev, dev);
2871ed2b3fcSJarkko Nikula 	struct dwc_pwm *dwc = pci_get_drvdata(pdev);
2881ed2b3fcSJarkko Nikula 	int i;
2891ed2b3fcSJarkko Nikula 
2901ed2b3fcSJarkko Nikula 	for (i = 0; i < DWC_TIMERS_TOTAL; i++) {
2911ed2b3fcSJarkko Nikula 		dwc_pwm_writel(dwc, dwc->ctx[i].cnt, DWC_TIM_LD_CNT(i));
2921ed2b3fcSJarkko Nikula 		dwc_pwm_writel(dwc, dwc->ctx[i].cnt2, DWC_TIM_LD_CNT2(i));
2931ed2b3fcSJarkko Nikula 		dwc_pwm_writel(dwc, dwc->ctx[i].ctrl, DWC_TIM_CTRL(i));
2941ed2b3fcSJarkko Nikula 	}
2951ed2b3fcSJarkko Nikula 
2961ed2b3fcSJarkko Nikula 	return 0;
2971ed2b3fcSJarkko Nikula }
2981ed2b3fcSJarkko Nikula #endif
2991ed2b3fcSJarkko Nikula 
3001ed2b3fcSJarkko Nikula static SIMPLE_DEV_PM_OPS(dwc_pwm_pm_ops, dwc_pwm_suspend, dwc_pwm_resume);
3011ed2b3fcSJarkko Nikula 
3021ed2b3fcSJarkko Nikula static const struct pci_device_id dwc_pwm_id_table[] = {
3031ed2b3fcSJarkko Nikula 	{ PCI_VDEVICE(INTEL, 0x4bb7) }, /* Elkhart Lake */
3041ed2b3fcSJarkko Nikula 	{  }	/* Terminating Entry */
3051ed2b3fcSJarkko Nikula };
3061ed2b3fcSJarkko Nikula MODULE_DEVICE_TABLE(pci, dwc_pwm_id_table);
3071ed2b3fcSJarkko Nikula 
3081ed2b3fcSJarkko Nikula static struct pci_driver dwc_pwm_driver = {
3091ed2b3fcSJarkko Nikula 	.name = "pwm-dwc",
3101ed2b3fcSJarkko Nikula 	.probe = dwc_pwm_probe,
3111ed2b3fcSJarkko Nikula 	.remove = dwc_pwm_remove,
3121ed2b3fcSJarkko Nikula 	.id_table = dwc_pwm_id_table,
3131ed2b3fcSJarkko Nikula 	.driver = {
3141ed2b3fcSJarkko Nikula 		.pm = &dwc_pwm_pm_ops,
3151ed2b3fcSJarkko Nikula 	},
3161ed2b3fcSJarkko Nikula };
3171ed2b3fcSJarkko Nikula 
3181ed2b3fcSJarkko Nikula module_pci_driver(dwc_pwm_driver);
3191ed2b3fcSJarkko Nikula 
3201ed2b3fcSJarkko Nikula MODULE_AUTHOR("Felipe Balbi (Intel)");
3211ed2b3fcSJarkko Nikula MODULE_AUTHOR("Jarkko Nikula <jarkko.nikula@linux.intel.com>");
3221ed2b3fcSJarkko Nikula MODULE_AUTHOR("Raymond Tan <raymond.tan@intel.com>");
3231ed2b3fcSJarkko Nikula MODULE_DESCRIPTION("DesignWare PWM Controller");
3241ed2b3fcSJarkko Nikula MODULE_LICENSE("GPL");
325