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 static int dwc_pwm_suspend(struct device *dev) 75 { 76 struct pci_dev *pdev = container_of(dev, struct pci_dev, dev); 77 struct dwc_pwm *dwc = pci_get_drvdata(pdev); 78 int i; 79 80 for (i = 0; i < DWC_TIMERS_TOTAL; i++) { 81 if (dwc->chip.pwms[i].state.enabled) { 82 dev_err(dev, "PWM %u in use by consumer (%s)\n", 83 i, dwc->chip.pwms[i].label); 84 return -EBUSY; 85 } 86 dwc->ctx[i].cnt = dwc_pwm_readl(dwc, DWC_TIM_LD_CNT(i)); 87 dwc->ctx[i].cnt2 = dwc_pwm_readl(dwc, DWC_TIM_LD_CNT2(i)); 88 dwc->ctx[i].ctrl = dwc_pwm_readl(dwc, DWC_TIM_CTRL(i)); 89 } 90 91 return 0; 92 } 93 94 static int dwc_pwm_resume(struct device *dev) 95 { 96 struct pci_dev *pdev = container_of(dev, struct pci_dev, dev); 97 struct dwc_pwm *dwc = pci_get_drvdata(pdev); 98 int i; 99 100 for (i = 0; i < DWC_TIMERS_TOTAL; i++) { 101 dwc_pwm_writel(dwc, dwc->ctx[i].cnt, DWC_TIM_LD_CNT(i)); 102 dwc_pwm_writel(dwc, dwc->ctx[i].cnt2, DWC_TIM_LD_CNT2(i)); 103 dwc_pwm_writel(dwc, dwc->ctx[i].ctrl, DWC_TIM_CTRL(i)); 104 } 105 106 return 0; 107 } 108 109 static DEFINE_SIMPLE_DEV_PM_OPS(dwc_pwm_pm_ops, dwc_pwm_suspend, dwc_pwm_resume); 110 111 static const struct pci_device_id dwc_pwm_id_table[] = { 112 { PCI_VDEVICE(INTEL, 0x4bb7) }, /* Elkhart Lake */ 113 { } /* Terminating Entry */ 114 }; 115 MODULE_DEVICE_TABLE(pci, dwc_pwm_id_table); 116 117 static struct pci_driver dwc_pwm_driver = { 118 .name = "pwm-dwc", 119 .probe = dwc_pwm_probe, 120 .remove = dwc_pwm_remove, 121 .id_table = dwc_pwm_id_table, 122 .driver = { 123 .pm = pm_ptr(&dwc_pwm_pm_ops), 124 }, 125 }; 126 127 module_pci_driver(dwc_pwm_driver); 128 129 MODULE_AUTHOR("Felipe Balbi (Intel)"); 130 MODULE_AUTHOR("Jarkko Nikula <jarkko.nikula@linux.intel.com>"); 131 MODULE_AUTHOR("Raymond Tan <raymond.tan@intel.com>"); 132 MODULE_DESCRIPTION("DesignWare PWM Controller"); 133 MODULE_LICENSE("GPL"); 134