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 /* Elkhart Lake */ 29 static const struct dwc_pwm_info ehl_pwm_info = { 30 .nr = 2, 31 .size = 0x1000, 32 }; 33 34 static int dwc_pwm_init_one(struct device *dev, void __iomem *base, unsigned int offset) 35 { 36 struct pwm_chip *chip; 37 struct dwc_pwm *dwc; 38 39 chip = dwc_pwm_alloc(dev); 40 if (IS_ERR(chip)) 41 return PTR_ERR(chip); 42 43 dwc = to_dwc_pwm(chip); 44 dwc->base = base + offset; 45 46 return devm_pwmchip_add(dev, chip); 47 } 48 49 static int dwc_pwm_probe(struct pci_dev *pci, const struct pci_device_id *id) 50 { 51 const struct dwc_pwm_info *info; 52 struct device *dev = &pci->dev; 53 int i, ret; 54 55 ret = pcim_enable_device(pci); 56 if (ret) 57 return dev_err_probe(dev, ret, "Failed to enable device\n"); 58 59 pci_set_master(pci); 60 61 ret = pcim_iomap_regions(pci, BIT(0), pci_name(pci)); 62 if (ret) 63 return dev_err_probe(dev, ret, "Failed to iomap PCI BAR\n"); 64 65 info = (const struct dwc_pwm_info *)id->driver_data; 66 67 for (i = 0; i < info->nr; i++) { 68 /* 69 * No need to check for pcim_iomap_table() failure, 70 * pcim_iomap_regions() already does it for us. 71 */ 72 ret = dwc_pwm_init_one(dev, pcim_iomap_table(pci)[0], i * info->size); 73 if (ret) 74 return ret; 75 } 76 77 pm_runtime_put(dev); 78 pm_runtime_allow(dev); 79 80 return 0; 81 } 82 83 static void dwc_pwm_remove(struct pci_dev *pci) 84 { 85 pm_runtime_forbid(&pci->dev); 86 pm_runtime_get_noresume(&pci->dev); 87 } 88 89 static int dwc_pwm_suspend(struct device *dev) 90 { 91 struct pwm_chip *chip = dev_get_drvdata(dev); 92 struct dwc_pwm *dwc = to_dwc_pwm(chip); 93 int i; 94 95 for (i = 0; i < DWC_TIMERS_TOTAL; i++) { 96 if (chip->pwms[i].state.enabled) { 97 dev_err(dev, "PWM %u in use by consumer (%s)\n", 98 i, chip->pwms[i].label); 99 return -EBUSY; 100 } 101 dwc->ctx[i].cnt = dwc_pwm_readl(dwc, DWC_TIM_LD_CNT(i)); 102 dwc->ctx[i].cnt2 = dwc_pwm_readl(dwc, DWC_TIM_LD_CNT2(i)); 103 dwc->ctx[i].ctrl = dwc_pwm_readl(dwc, DWC_TIM_CTRL(i)); 104 } 105 106 return 0; 107 } 108 109 static int dwc_pwm_resume(struct device *dev) 110 { 111 struct pwm_chip *chip = dev_get_drvdata(dev); 112 struct dwc_pwm *dwc = to_dwc_pwm(chip); 113 int i; 114 115 for (i = 0; i < DWC_TIMERS_TOTAL; i++) { 116 dwc_pwm_writel(dwc, dwc->ctx[i].cnt, DWC_TIM_LD_CNT(i)); 117 dwc_pwm_writel(dwc, dwc->ctx[i].cnt2, DWC_TIM_LD_CNT2(i)); 118 dwc_pwm_writel(dwc, dwc->ctx[i].ctrl, DWC_TIM_CTRL(i)); 119 } 120 121 return 0; 122 } 123 124 static DEFINE_SIMPLE_DEV_PM_OPS(dwc_pwm_pm_ops, dwc_pwm_suspend, dwc_pwm_resume); 125 126 static const struct pci_device_id dwc_pwm_id_table[] = { 127 { PCI_VDEVICE(INTEL, 0x4bb7), (kernel_ulong_t)&ehl_pwm_info }, 128 { } /* Terminating Entry */ 129 }; 130 MODULE_DEVICE_TABLE(pci, dwc_pwm_id_table); 131 132 static struct pci_driver dwc_pwm_driver = { 133 .name = "pwm-dwc", 134 .probe = dwc_pwm_probe, 135 .remove = dwc_pwm_remove, 136 .id_table = dwc_pwm_id_table, 137 .driver = { 138 .pm = pm_sleep_ptr(&dwc_pwm_pm_ops), 139 }, 140 }; 141 142 module_pci_driver(dwc_pwm_driver); 143 144 MODULE_AUTHOR("Felipe Balbi (Intel)"); 145 MODULE_AUTHOR("Jarkko Nikula <jarkko.nikula@linux.intel.com>"); 146 MODULE_AUTHOR("Raymond Tan <raymond.tan@intel.com>"); 147 MODULE_DESCRIPTION("DesignWare PWM Controller"); 148 MODULE_LICENSE("GPL"); 149