Merge branch 'address-masking'
[linux-2.6-microblaze.git] / drivers / pwm / pwm-dwc-core.c
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * DesignWare PWM Controller driver core
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
12 #define DEFAULT_SYMBOL_NAMESPACE dwc_pwm
13
14 #include <linux/bitops.h>
15 #include <linux/export.h>
16 #include <linux/kernel.h>
17 #include <linux/module.h>
18 #include <linux/pci.h>
19 #include <linux/pm_runtime.h>
20 #include <linux/pwm.h>
21
22 #include "pwm-dwc.h"
23
24 static void __dwc_pwm_set_enable(struct dwc_pwm *dwc, int pwm, int enabled)
25 {
26         u32 reg;
27
28         reg = dwc_pwm_readl(dwc, DWC_TIM_CTRL(pwm));
29
30         if (enabled)
31                 reg |= DWC_TIM_CTRL_EN;
32         else
33                 reg &= ~DWC_TIM_CTRL_EN;
34
35         dwc_pwm_writel(dwc, reg, DWC_TIM_CTRL(pwm));
36 }
37
38 static int __dwc_pwm_configure_timer(struct dwc_pwm *dwc,
39                                      struct pwm_device *pwm,
40                                      const struct pwm_state *state)
41 {
42         u64 tmp;
43         u32 ctrl;
44         u32 high;
45         u32 low;
46
47         /*
48          * Calculate width of low and high period in terms of input clock
49          * periods and check are the result within HW limits between 1 and
50          * 2^32 periods.
51          */
52         tmp = DIV_ROUND_CLOSEST_ULL(state->duty_cycle, dwc->clk_ns);
53         if (tmp < 1 || tmp > (1ULL << 32))
54                 return -ERANGE;
55         low = tmp - 1;
56
57         tmp = DIV_ROUND_CLOSEST_ULL(state->period - state->duty_cycle,
58                                     dwc->clk_ns);
59         if (tmp < 1 || tmp > (1ULL << 32))
60                 return -ERANGE;
61         high = tmp - 1;
62
63         /*
64          * Specification says timer usage flow is to disable timer, then
65          * program it followed by enable. It also says Load Count is loaded
66          * into timer after it is enabled - either after a disable or
67          * a reset. Based on measurements it happens also without disable
68          * whenever Load Count is updated. But follow the specification.
69          */
70         __dwc_pwm_set_enable(dwc, pwm->hwpwm, false);
71
72         /*
73          * Write Load Count and Load Count 2 registers. Former defines the
74          * width of low period and latter the width of high period in terms
75          * multiple of input clock periods:
76          * Width = ((Count + 1) * input clock period).
77          */
78         dwc_pwm_writel(dwc, low, DWC_TIM_LD_CNT(pwm->hwpwm));
79         dwc_pwm_writel(dwc, high, DWC_TIM_LD_CNT2(pwm->hwpwm));
80
81         /*
82          * Set user-defined mode, timer reloads from Load Count registers
83          * when it counts down to 0.
84          * Set PWM mode, it makes output to toggle and width of low and high
85          * periods are set by Load Count registers.
86          */
87         ctrl = DWC_TIM_CTRL_MODE_USER | DWC_TIM_CTRL_PWM;
88         dwc_pwm_writel(dwc, ctrl, DWC_TIM_CTRL(pwm->hwpwm));
89
90         /*
91          * Enable timer. Output starts from low period.
92          */
93         __dwc_pwm_set_enable(dwc, pwm->hwpwm, state->enabled);
94
95         return 0;
96 }
97
98 static int dwc_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
99                          const struct pwm_state *state)
100 {
101         struct dwc_pwm *dwc = to_dwc_pwm(chip);
102
103         if (state->polarity != PWM_POLARITY_INVERSED)
104                 return -EINVAL;
105
106         if (state->enabled) {
107                 if (!pwm->state.enabled)
108                         pm_runtime_get_sync(pwmchip_parent(chip));
109                 return __dwc_pwm_configure_timer(dwc, pwm, state);
110         } else {
111                 if (pwm->state.enabled) {
112                         __dwc_pwm_set_enable(dwc, pwm->hwpwm, false);
113                         pm_runtime_put_sync(pwmchip_parent(chip));
114                 }
115         }
116
117         return 0;
118 }
119
120 static int dwc_pwm_get_state(struct pwm_chip *chip, struct pwm_device *pwm,
121                              struct pwm_state *state)
122 {
123         struct dwc_pwm *dwc = to_dwc_pwm(chip);
124         u64 duty, period;
125         u32 ctrl, ld, ld2;
126
127         pm_runtime_get_sync(pwmchip_parent(chip));
128
129         ctrl = dwc_pwm_readl(dwc, DWC_TIM_CTRL(pwm->hwpwm));
130         ld = dwc_pwm_readl(dwc, DWC_TIM_LD_CNT(pwm->hwpwm));
131         ld2 = dwc_pwm_readl(dwc, DWC_TIM_LD_CNT2(pwm->hwpwm));
132
133         state->enabled = !!(ctrl & DWC_TIM_CTRL_EN);
134
135         /*
136          * If we're not in PWM, technically the output is a 50-50
137          * based on the timer load-count only.
138          */
139         if (ctrl & DWC_TIM_CTRL_PWM) {
140                 duty = (ld + 1) * dwc->clk_ns;
141                 period = (ld2 + 1)  * dwc->clk_ns;
142                 period += duty;
143         } else {
144                 duty = (ld + 1) * dwc->clk_ns;
145                 period = duty * 2;
146         }
147
148         state->polarity = PWM_POLARITY_INVERSED;
149         state->period = period;
150         state->duty_cycle = duty;
151
152         pm_runtime_put_sync(pwmchip_parent(chip));
153
154         return 0;
155 }
156
157 static const struct pwm_ops dwc_pwm_ops = {
158         .apply = dwc_pwm_apply,
159         .get_state = dwc_pwm_get_state,
160 };
161
162 struct pwm_chip *dwc_pwm_alloc(struct device *dev)
163 {
164         struct pwm_chip *chip;
165         struct dwc_pwm *dwc;
166
167         chip = devm_pwmchip_alloc(dev, DWC_TIMERS_TOTAL, sizeof(*dwc));
168         if (IS_ERR(chip))
169                 return chip;
170         dwc = to_dwc_pwm(chip);
171
172         dwc->clk_ns = 10;
173         chip->ops = &dwc_pwm_ops;
174
175         dev_set_drvdata(dev, chip);
176         return chip;
177 }
178 EXPORT_SYMBOL_GPL(dwc_pwm_alloc);
179
180 MODULE_AUTHOR("Felipe Balbi (Intel)");
181 MODULE_AUTHOR("Jarkko Nikula <jarkko.nikula@linux.intel.com>");
182 MODULE_AUTHOR("Raymond Tan <raymond.tan@intel.com>");
183 MODULE_DESCRIPTION("DesignWare PWM Controller");
184 MODULE_LICENSE("GPL");