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