1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * DesignWare PWM Controller driver (PCI part) 4 * 5 * Copyright (C) 2018-2020 Intel Corporation 6 * 7 * Author: Felipe Balbi (Intel) 8 * Author: Jarkko Nikula <jarkko.nikula@linux.intel.com> 9 * Author: Raymond Tan <raymond.tan@intel.com> 10 * 11 * Limitations: 12 * - The hardware cannot generate a 0 % or 100 % duty cycle. Both high and low 13 * periods are one or more input clock periods long. 14 */ 15 16 #define DEFAULT_MOUDLE_NAMESPACE dwc_pwm 17 18 #include <linux/bitops.h> 19 #include <linux/export.h> 20 #include <linux/kernel.h> 21 #include <linux/module.h> 22 #include <linux/pci.h> 23 #include <linux/pm_runtime.h> 24 #include <linux/pwm.h> 25 26 #include "pwm-dwc.h" 27 28 static int dwc_pwm_probe(struct pci_dev *pci, const struct pci_device_id *id) 29 { 30 struct device *dev = &pci->dev; 31 struct dwc_pwm *dwc; 32 int ret; 33 34 dwc = dwc_pwm_alloc(dev); 35 if (!dwc) 36 return -ENOMEM; 37 38 ret = pcim_enable_device(pci); 39 if (ret) { 40 dev_err(dev, "Failed to enable device (%pe)\n", ERR_PTR(ret)); 41 return ret; 42 } 43 44 pci_set_master(pci); 45 46 ret = pcim_iomap_regions(pci, BIT(0), pci_name(pci)); 47 if (ret) { 48 dev_err(dev, "Failed to iomap PCI BAR (%pe)\n", ERR_PTR(ret)); 49 return ret; 50 } 51 52 dwc->base = pcim_iomap_table(pci)[0]; 53 if (!dwc->base) { 54 dev_err(dev, "Base address missing\n"); 55 return -ENOMEM; 56 } 57 58 ret = devm_pwmchip_add(dev, &dwc->chip); 59 if (ret) 60 return ret; 61 62 pm_runtime_put(dev); 63 pm_runtime_allow(dev); 64 65 return 0; 66 } 67 68 static void dwc_pwm_remove(struct pci_dev *pci) 69 { 70 pm_runtime_forbid(&pci->dev); 71 pm_runtime_get_noresume(&pci->dev); 72 } 73 74 #ifdef CONFIG_PM_SLEEP 75 static int dwc_pwm_suspend(struct device *dev) 76 { 77 struct pci_dev *pdev = container_of(dev, struct pci_dev, dev); 78 struct dwc_pwm *dwc = pci_get_drvdata(pdev); 79 int i; 80 81 for (i = 0; i < DWC_TIMERS_TOTAL; i++) { 82 if (dwc->chip.pwms[i].state.enabled) { 83 dev_err(dev, "PWM %u in use by consumer (%s)\n", 84 i, dwc->chip.pwms[i].label); 85 return -EBUSY; 86 } 87 dwc->ctx[i].cnt = dwc_pwm_readl(dwc, DWC_TIM_LD_CNT(i)); 88 dwc->ctx[i].cnt2 = dwc_pwm_readl(dwc, DWC_TIM_LD_CNT2(i)); 89 dwc->ctx[i].ctrl = dwc_pwm_readl(dwc, DWC_TIM_CTRL(i)); 90 } 91 92 return 0; 93 } 94 95 static int dwc_pwm_resume(struct device *dev) 96 { 97 struct pci_dev *pdev = container_of(dev, struct pci_dev, dev); 98 struct dwc_pwm *dwc = pci_get_drvdata(pdev); 99 int i; 100 101 for (i = 0; i < DWC_TIMERS_TOTAL; i++) { 102 dwc_pwm_writel(dwc, dwc->ctx[i].cnt, DWC_TIM_LD_CNT(i)); 103 dwc_pwm_writel(dwc, dwc->ctx[i].cnt2, DWC_TIM_LD_CNT2(i)); 104 dwc_pwm_writel(dwc, dwc->ctx[i].ctrl, DWC_TIM_CTRL(i)); 105 } 106 107 return 0; 108 } 109 #endif 110 111 static SIMPLE_DEV_PM_OPS(dwc_pwm_pm_ops, dwc_pwm_suspend, dwc_pwm_resume); 112 113 static const struct pci_device_id dwc_pwm_id_table[] = { 114 { PCI_VDEVICE(INTEL, 0x4bb7) }, /* Elkhart Lake */ 115 { } /* Terminating Entry */ 116 }; 117 MODULE_DEVICE_TABLE(pci, dwc_pwm_id_table); 118 119 static struct pci_driver dwc_pwm_driver = { 120 .name = "pwm-dwc", 121 .probe = dwc_pwm_probe, 122 .remove = dwc_pwm_remove, 123 .id_table = dwc_pwm_id_table, 124 .driver = { 125 .pm = &dwc_pwm_pm_ops, 126 }, 127 }; 128 129 module_pci_driver(dwc_pwm_driver); 130 131 MODULE_AUTHOR("Felipe Balbi (Intel)"); 132 MODULE_AUTHOR("Jarkko Nikula <jarkko.nikula@linux.intel.com>"); 133 MODULE_AUTHOR("Raymond Tan <raymond.tan@intel.com>"); 134 MODULE_DESCRIPTION("DesignWare PWM Controller"); 135 MODULE_LICENSE("GPL"); 136