Commit | Line | Data |
---|---|---|
1ed2b3fc JN |
1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* | |
721ee188 | 3 | * DesignWare PWM Controller driver (PCI part) |
1ed2b3fc JN |
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 | ||
721ee188 BD |
16 | #define DEFAULT_MOUDLE_NAMESPACE dwc_pwm |
17 | ||
1ed2b3fc JN |
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 | ||
721ee188 | 26 | #include "pwm-dwc.h" |
a357d149 | 27 | |
ebf2c89e RJ |
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) | |
1ed2b3fc | 35 | { |
aaa3cc29 | 36 | struct pwm_chip *chip; |
1ed2b3fc | 37 | struct dwc_pwm *dwc; |
1ed2b3fc | 38 | |
aaa3cc29 UKK |
39 | chip = dwc_pwm_alloc(dev); |
40 | if (IS_ERR(chip)) | |
41 | return PTR_ERR(chip); | |
ebf2c89e | 42 | |
aaa3cc29 | 43 | dwc = to_dwc_pwm(chip); |
ebf2c89e RJ |
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; | |
1ed2b3fc JN |
54 | |
55 | ret = pcim_enable_device(pci); | |
9e3440d2 RJ |
56 | if (ret) |
57 | return dev_err_probe(dev, ret, "Failed to enable device\n"); | |
1ed2b3fc JN |
58 | |
59 | pci_set_master(pci); | |
60 | ||
61 | ret = pcim_iomap_regions(pci, BIT(0), pci_name(pci)); | |
9e3440d2 RJ |
62 | if (ret) |
63 | return dev_err_probe(dev, ret, "Failed to iomap PCI BAR\n"); | |
1ed2b3fc | 64 | |
ebf2c89e | 65 | info = (const struct dwc_pwm_info *)id->driver_data; |
1ed2b3fc | 66 | |
ebf2c89e RJ |
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 | } | |
1ed2b3fc JN |
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 | { | |
1ed2b3fc JN |
85 | pm_runtime_forbid(&pci->dev); |
86 | pm_runtime_get_noresume(&pci->dev); | |
1ed2b3fc JN |
87 | } |
88 | ||
1ed2b3fc JN |
89 | static int dwc_pwm_suspend(struct device *dev) |
90 | { | |
aaa3cc29 UKK |
91 | struct pwm_chip *chip = dev_get_drvdata(dev); |
92 | struct dwc_pwm *dwc = to_dwc_pwm(chip); | |
1ed2b3fc JN |
93 | int i; |
94 | ||
95 | for (i = 0; i < DWC_TIMERS_TOTAL; i++) { | |
aaa3cc29 | 96 | if (chip->pwms[i].state.enabled) { |
1ed2b3fc | 97 | dev_err(dev, "PWM %u in use by consumer (%s)\n", |
aaa3cc29 | 98 | i, chip->pwms[i].label); |
1ed2b3fc JN |
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 | { | |
aaa3cc29 UKK |
111 | struct pwm_chip *chip = dev_get_drvdata(dev); |
112 | struct dwc_pwm *dwc = to_dwc_pwm(chip); | |
1ed2b3fc JN |
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 | } | |
1ed2b3fc | 123 | |
30b5b066 | 124 | static DEFINE_SIMPLE_DEV_PM_OPS(dwc_pwm_pm_ops, dwc_pwm_suspend, dwc_pwm_resume); |
1ed2b3fc JN |
125 | |
126 | static const struct pci_device_id dwc_pwm_id_table[] = { | |
ebf2c89e | 127 | { PCI_VDEVICE(INTEL, 0x4bb7), (kernel_ulong_t)&ehl_pwm_info }, |
1ed2b3fc JN |
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 = { | |
7cfce2b8 | 138 | .pm = pm_sleep_ptr(&dwc_pwm_pm_ops), |
1ed2b3fc JN |
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"); |