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