Skip to content

Commit 721ee18

Browse files
bjdooks-ctthierryreding
authored andcommitted
pwm: dwc: split pci out of core driver
Moving towards adding non-pci support for the driver, move the pci parts out of the core into their own module. This is partly due to the module_driver() code only being allowed once in a module and also to avoid a number of #ifdef if we build a single file in a system without pci support. Signed-off-by: Ben Dooks <ben.dooks@codethink.co.uk> Acked-by: Uwe Kleine-König <u.kleine-koenig@pengutronix.de> Link: https://lore.kernel.org/r/20230907161242.67190-2-ben.dooks@codethink.co.uk Signed-off-by: Uwe Kleine-König <u.kleine-koenig@pengutronix.de> Signed-off-by: Thierry Reding <thierry.reding@gmail.com>
1 parent 6dbf23f commit 721ee18

5 files changed

Lines changed: 252 additions & 194 deletions

File tree

drivers/pwm/Kconfig

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -186,9 +186,19 @@ config PWM_CROS_EC
186186
PWM driver for exposing a PWM attached to the ChromeOS Embedded
187187
Controller.
188188

189+
config PWM_DWC_CORE
190+
tristate
191+
depends on HAS_IOMEM
192+
help
193+
PWM driver for Synopsys DWC PWM Controller.
194+
195+
To compile this driver as a module, build the dependecies as
196+
modules, this will be called pwm-dwc-core.
197+
189198
config PWM_DWC
190-
tristate "DesignWare PWM Controller"
191-
depends on PCI
199+
tristate "DesignWare PWM Controller (PCI bus)"
200+
depends on HAS_IOMEM && PCI
201+
select PWM_DWC_CORE
192202
help
193203
PWM driver for Synopsys DWC PWM Controller attached to a PCI bus.
194204

drivers/pwm/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ obj-$(CONFIG_PWM_CLK) += pwm-clk.o
1515
obj-$(CONFIG_PWM_CLPS711X) += pwm-clps711x.o
1616
obj-$(CONFIG_PWM_CRC) += pwm-crc.o
1717
obj-$(CONFIG_PWM_CROS_EC) += pwm-cros-ec.o
18+
obj-$(CONFIG_PWM_DWC_CORE) += pwm-dwc-core.o
1819
obj-$(CONFIG_PWM_DWC) += pwm-dwc.o
1920
obj-$(CONFIG_PWM_EP93XX) += pwm-ep93xx.o
2021
obj-$(CONFIG_PWM_FSL_FTM) += pwm-fsl-ftm.o

drivers/pwm/pwm-dwc-core.c

Lines changed: 175 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,175 @@
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_PERIOD_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_PERIOD_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(chip->dev);
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(chip->dev);
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+
126+
pm_runtime_get_sync(chip->dev);
127+
128+
state->enabled = !!(dwc_pwm_readl(dwc,
129+
DWC_TIM_CTRL(pwm->hwpwm)) & DWC_TIM_CTRL_EN);
130+
131+
duty = dwc_pwm_readl(dwc, DWC_TIM_LD_CNT(pwm->hwpwm));
132+
duty += 1;
133+
duty *= DWC_CLK_PERIOD_NS;
134+
state->duty_cycle = duty;
135+
136+
period = dwc_pwm_readl(dwc, DWC_TIM_LD_CNT2(pwm->hwpwm));
137+
period += 1;
138+
period *= DWC_CLK_PERIOD_NS;
139+
period += duty;
140+
state->period = period;
141+
142+
state->polarity = PWM_POLARITY_INVERSED;
143+
144+
pm_runtime_put_sync(chip->dev);
145+
146+
return 0;
147+
}
148+
149+
static const struct pwm_ops dwc_pwm_ops = {
150+
.apply = dwc_pwm_apply,
151+
.get_state = dwc_pwm_get_state,
152+
};
153+
154+
struct dwc_pwm *dwc_pwm_alloc(struct device *dev)
155+
{
156+
struct dwc_pwm *dwc;
157+
158+
dwc = devm_kzalloc(dev, sizeof(*dwc), GFP_KERNEL);
159+
if (!dwc)
160+
return NULL;
161+
162+
dwc->chip.dev = dev;
163+
dwc->chip.ops = &dwc_pwm_ops;
164+
dwc->chip.npwm = DWC_TIMERS_TOTAL;
165+
166+
dev_set_drvdata(dev, dwc);
167+
return dwc;
168+
}
169+
EXPORT_SYMBOL_GPL(dwc_pwm_alloc);
170+
171+
MODULE_AUTHOR("Felipe Balbi (Intel)");
172+
MODULE_AUTHOR("Jarkko Nikula <jarkko.nikula@linux.intel.com>");
173+
MODULE_AUTHOR("Raymond Tan <raymond.tan@intel.com>");
174+
MODULE_DESCRIPTION("DesignWare PWM Controller");
175+
MODULE_LICENSE("GPL");

0 commit comments

Comments
 (0)