Linux Audio

Check our new training course

Loading...
Note: File does not exist in v3.5.6.
  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
 24static 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
 38static 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
 98static 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
120static 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
157static const struct pwm_ops dwc_pwm_ops = {
158	.apply = dwc_pwm_apply,
159	.get_state = dwc_pwm_get_state,
160};
161
162struct 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	return chip;
176}
177EXPORT_SYMBOL_GPL(dwc_pwm_alloc);
178
179MODULE_AUTHOR("Felipe Balbi (Intel)");
180MODULE_AUTHOR("Jarkko Nikula <jarkko.nikula@linux.intel.com>");
181MODULE_AUTHOR("Raymond Tan <raymond.tan@intel.com>");
182MODULE_DESCRIPTION("DesignWare PWM Controller");
183MODULE_LICENSE("GPL");