1
0
Fork 0

pwm: Changes for v4.3-rc1

This set of changes introduces the beginnings of a new API that's based
 around the concept of states that can be atomically applied. Drivers go
 to various lengths to implement something similar, which indicates that
 the core should really be providing the necessary framework.
 
 On top of that, there is a bit of cleanup as well as improved kerneldoc
 and integration into the device-drivers DocBook.
 
 Regarding drivers there is a new one for the NXP LPC18xx family of SoCs
 and a couple of fixes for existing drivers (pca9685, Broadcom Kona and
 Atmel HLCDC).
 -----BEGIN PGP SIGNATURE-----
 Version: GnuPG v2
 
 iQIcBAABCAAGBQJV8DLwAAoJEN0jrNd/PrOhSf4P/RYwdDGB/xvI0ifXS5p9znNA
 hr6+MUH87+f9NY1jcDye0RrxIkRsckQ3x3fe/nTmODjCSM/cyrWrVAEKbkqE5bZH
 LTRT6M6jolSAUGXFTBDuHktYdMU9yaD0PJvm0RgBEC7z2BeBmCgCXTFc4paxw2GA
 NvikouXZX9XDlXp598pHPFoL92vTLevQQMBHYW9myY8cjAOG0X8Dn05xQaC2kRvJ
 5q+OD1D/kIEMJxONrpef7bwzPgCkuGTHqqIIm6gLVoPBPGWz43kaZ5+x/j2E98E9
 fkfcP+21GvZ/tdAUEkrRnpy5Jnn+Nk0m+yzPtRkvj4TLwPd93EjumiPg5sb53pys
 ZfoHgnOAArVxEnTYLQk4IyBWcHyyzIxZBKZZpXkMXjY4QVq6wZLAP+vyCAzcm4tV
 vHqUN5BYAkwYPUNU58fgFWGHg6rFlb2QSN1bIj5opgygWmRbPgmwVj3trvS2Dkoe
 NOv47pp0OyWvTvx+wLql8BUt1zK1M6QldfPF6pR4FB18SLyKYH89ZbjHTyGdsuCv
 SiaWrqNB4eRmEKW1MA20Kc4wlF5c2peXVp2EHeddh/oHPES8IVWBPMPwSkJZrpQb
 bIUMStKlJ/Y1GPo6Dd7njK/kA3GwwsbYXYHmyPm0IRuAeZKrzGzVSIIljbfpL6Vf
 LBuKRMQHY4iWtF8A5SmY
 =ymH8
 -----END PGP SIGNATURE-----

Merge tag 'pwm/for-4.3-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/thierry.reding/linux-pwm

Pull pwm updates from Thierry Reding:
 "This set of changes introduces the beginnings of a new API that's
  based around the concept of states that can be atomically applied.
  Drivers go to various lengths to implement something similar, which
  indicates that the core should really be providing the necessary
  framework.

  On top of that, there is a bit of cleanup as well as improved
  kerneldoc and integration into the device-drivers DocBook.

  Regarding drivers there is a new one for the NXP LPC18xx family of
  SoCs and a couple of fixes for existing drivers (pca9685, Broadcom
  Kona and Atmel HLCDC)"

* tag 'pwm/for-4.3-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/thierry.reding/linux-pwm:
  ARM: at91: pwm: atmel-hlcdc: Add at91sam9n12 errata
  pwm: Add NXP LPC18xx PWM/SCT DT binding documentation
  pwm: NXP LPC18xx PWM/SCT driver
  pwm-pca9685: Support changing the output frequency
  pwm-pca9685: Fix several driver bugs
  pwm: kona: Modify settings application sequence
  pwm: pca9685: Drop owner assignment
  pwm: Add to device-drivers documentation
  pwm: Clean up kerneldoc
  pwm: Remove useless whitespace
  pwm: sysfs: Remove unnecessary padding
  pwm: sysfs: Properly convert from enum to string
  pwm: Make use of pwm_get_xxx() helpers where appropriate
  pwm: Add pwm_get_polarity() helper function
  pwm: Constify PWM device where possible
  pwm: Add the pwm_is_enabled() helper
steinar/wifi_calib_4_9_kernel
Linus Torvalds 2015-09-09 10:55:32 -07:00
commit 82278fc079
21 changed files with 799 additions and 93 deletions

View File

@ -490,4 +490,31 @@ X!Ilib/fonts/fonts.c
!Edrivers/hsi/hsi.c
</chapter>
<chapter id="pwm">
<title>Pulse-Width Modulation (PWM)</title>
<para>
Pulse-width modulation is a modulation technique primarily used to
control power supplied to electrical devices.
</para>
<para>
The PWM framework provides an abstraction for providers and consumers
of PWM signals. A controller that provides one or more PWM signals is
registered as <structname>struct pwm_chip</structname>. Providers are
expected to embed this structure in a driver-specific structure. This
structure contains fields that describe a particular chip.
</para>
<para>
A chip exposes one or more PWM signal sources, each of which exposed
as a <structname>struct pwm_device</structname>. Operations can be
performed on PWM devices to control the period, duty cycle, polarity
and active state of the signal.
</para>
<para>
Note that PWM devices are exclusive resources: they can always only be
used by one consumer at a time.
</para>
!Iinclude/linux/pwm.h
!Edrivers/pwm/core.c
</chapter>
</book>

View File

@ -0,0 +1,20 @@
* NXP LPC18xx State Configurable Timer - Pulse Width Modulator driver
Required properties:
- compatible: Should be "nxp,lpc1850-sct-pwm"
- reg: Should contain physical base address and length of pwm registers.
- clocks: Must contain an entry for each entry in clock-names.
See ../clock/clock-bindings.txt for details.
- clock-names: Must include the following entries.
- pwm: PWM operating clock.
- #pwm-cells: Should be 3. See pwm.txt in this directory for the description
of the cells format.
Example:
pwm: pwm@40000000 {
compatible = "nxp,lpc1850-sct-pwm";
reg = <0x40000000 0x1000>;
clocks =<&ccu1 CLK_CPU_SCT>;
clock-names = "pwm";
#pwm-cells = <3>;
};

View File

@ -180,6 +180,18 @@ config PWM_LP3943
To compile this driver as a module, choose M here: the module
will be called pwm-lp3943.
config PWM_LPC18XX_SCT
tristate "LPC18xx/43xx PWM/SCT support"
depends on ARCH_LPC18XX
help
Generic PWM framework driver for NXP LPC18xx PWM/SCT which
supports 16 channels.
A maximum of 15 channels can be requested simultaneously and
must have the same period.
To compile this driver as a module, choose M here: the module
will be called pwm-lpc18xx-sct.
config PWM_LPC32XX
tristate "LPC32XX PWM support"
depends on ARCH_LPC32XX

View File

@ -15,6 +15,7 @@ obj-$(CONFIG_PWM_IMG) += pwm-img.o
obj-$(CONFIG_PWM_IMX) += pwm-imx.o
obj-$(CONFIG_PWM_JZ4740) += pwm-jz4740.o
obj-$(CONFIG_PWM_LP3943) += pwm-lp3943.o
obj-$(CONFIG_PWM_LPC18XX_SCT) += pwm-lpc18xx-sct.o
obj-$(CONFIG_PWM_LPC32XX) += pwm-lpc32xx.o
obj-$(CONFIG_PWM_LPSS) += pwm-lpss.o
obj-$(CONFIG_PWM_LPSS_PCI) += pwm-lpss-pci.o

View File

@ -200,6 +200,8 @@ static void of_pwmchip_remove(struct pwm_chip *chip)
* pwm_set_chip_data() - set private chip data for a PWM
* @pwm: PWM device
* @data: pointer to chip-specific data
*
* Returns: 0 on success or a negative error code on failure.
*/
int pwm_set_chip_data(struct pwm_device *pwm, void *data)
{
@ -215,6 +217,8 @@ EXPORT_SYMBOL_GPL(pwm_set_chip_data);
/**
* pwm_get_chip_data() - get private chip data for a PWM
* @pwm: PWM device
*
* Returns: A pointer to the chip-private data for the PWM device.
*/
void *pwm_get_chip_data(struct pwm_device *pwm)
{
@ -230,6 +234,8 @@ EXPORT_SYMBOL_GPL(pwm_get_chip_data);
* Register a new PWM chip. If chip->base < 0 then a dynamically assigned base
* will be used. The initial polarity for all channels is specified by the
* @polarity parameter.
*
* Returns: 0 on success or a negative error code on failure.
*/
int pwmchip_add_with_polarity(struct pwm_chip *chip,
enum pwm_polarity polarity)
@ -291,6 +297,8 @@ EXPORT_SYMBOL_GPL(pwmchip_add_with_polarity);
*
* Register a new PWM chip. If chip->base < 0 then a dynamically assigned base
* will be used. The initial polarity for all channels is normal.
*
* Returns: 0 on success or a negative error code on failure.
*/
int pwmchip_add(struct pwm_chip *chip)
{
@ -304,6 +312,8 @@ EXPORT_SYMBOL_GPL(pwmchip_add);
*
* Removes a PWM chip. This function may return busy if the PWM chip provides
* a PWM device that is still requested.
*
* Returns: 0 on success or a negative error code on failure.
*/
int pwmchip_remove(struct pwm_chip *chip)
{
@ -338,10 +348,13 @@ EXPORT_SYMBOL_GPL(pwmchip_remove);
/**
* pwm_request() - request a PWM device
* @pwm_id: global PWM device index
* @pwm: global PWM device index
* @label: PWM device label
*
* This function is deprecated, use pwm_get() instead.
*
* Returns: A pointer to a PWM device or an ERR_PTR()-encoded error code on
* failure.
*/
struct pwm_device *pwm_request(int pwm, const char *label)
{
@ -376,9 +389,9 @@ EXPORT_SYMBOL_GPL(pwm_request);
* @index: per-chip index of the PWM to request
* @label: a literal description string of this PWM
*
* Returns the PWM at the given index of the given PWM chip. A negative error
* code is returned if the index is not valid for the specified PWM chip or
* if the PWM device cannot be requested.
* Returns: A pointer to the PWM device at the given index of the given PWM
* chip. A negative error code is returned if the index is not valid for the
* specified PWM chip or if the PWM device cannot be requested.
*/
struct pwm_device *pwm_request_from_chip(struct pwm_chip *chip,
unsigned int index,
@ -419,6 +432,8 @@ EXPORT_SYMBOL_GPL(pwm_free);
* @pwm: PWM device
* @duty_ns: "on" time (in nanoseconds)
* @period_ns: duration (in nanoseconds) of one cycle
*
* Returns: 0 on success or a negative error code on failure.
*/
int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns)
{
@ -443,7 +458,10 @@ EXPORT_SYMBOL_GPL(pwm_config);
* @pwm: PWM device
* @polarity: new polarity of the PWM signal
*
* Note that the polarity cannot be configured while the PWM device is enabled
* Note that the polarity cannot be configured while the PWM device is
* enabled.
*
* Returns: 0 on success or a negative error code on failure.
*/
int pwm_set_polarity(struct pwm_device *pwm, enum pwm_polarity polarity)
{
@ -455,7 +473,7 @@ int pwm_set_polarity(struct pwm_device *pwm, enum pwm_polarity polarity)
if (!pwm->chip->ops->set_polarity)
return -ENOSYS;
if (test_bit(PWMF_ENABLED, &pwm->flags))
if (pwm_is_enabled(pwm))
return -EBUSY;
err = pwm->chip->ops->set_polarity(pwm->chip, pwm, polarity);
@ -471,6 +489,8 @@ EXPORT_SYMBOL_GPL(pwm_set_polarity);
/**
* pwm_enable() - start a PWM output toggling
* @pwm: PWM device
*
* Returns: 0 on success or a negative error code on failure.
*/
int pwm_enable(struct pwm_device *pwm)
{
@ -524,6 +544,9 @@ static struct pwm_chip *of_node_to_pwmchip(struct device_node *np)
* lookup of the PWM index. This also means that the "pwm-names" property
* becomes mandatory for devices that look up the PWM device via the con_id
* parameter.
*
* Returns: A pointer to the requested PWM device or an ERR_PTR()-encoded
* error code on failure.
*/
struct pwm_device *of_pwm_get(struct device_node *np, const char *con_id)
{
@ -630,6 +653,9 @@ void pwm_remove_table(struct pwm_lookup *table, size_t num)
*
* Once a PWM chip has been found the specified PWM device will be requested
* and is ready to be used.
*
* Returns: A pointer to the requested PWM device or an ERR_PTR()-encoded
* error code on failure.
*/
struct pwm_device *pwm_get(struct device *dev, const char *con_id)
{
@ -752,6 +778,9 @@ static void devm_pwm_release(struct device *dev, void *res)
*
* This function performs like pwm_get() but the acquired PWM device will
* automatically be released on driver detach.
*
* Returns: A pointer to the requested PWM device or an ERR_PTR()-encoded
* error code on failure.
*/
struct pwm_device *devm_pwm_get(struct device *dev, const char *con_id)
{
@ -781,6 +810,9 @@ EXPORT_SYMBOL_GPL(devm_pwm_get);
*
* This function performs like of_pwm_get() but the acquired PWM device will
* automatically be released on driver detach.
*
* Returns: A pointer to the requested PWM device or an ERR_PTR()-encoded
* error code on failure.
*/
struct pwm_device *devm_of_pwm_get(struct device *dev, struct device_node *np,
const char *con_id)
@ -832,7 +864,7 @@ EXPORT_SYMBOL_GPL(devm_pwm_put);
* pwm_can_sleep() - report whether PWM access will sleep
* @pwm: PWM device
*
* It returns true if accessing the PWM can sleep, false otherwise.
* Returns: True if accessing the PWM can sleep, false otherwise.
*/
bool pwm_can_sleep(struct pwm_device *pwm)
{
@ -853,7 +885,7 @@ static void pwm_dbg_show(struct pwm_chip *chip, struct seq_file *s)
if (test_bit(PWMF_REQUESTED, &pwm->flags))
seq_puts(s, " requested");
if (test_bit(PWMF_ENABLED, &pwm->flags))
if (pwm_is_enabled(pwm))
seq_puts(s, " enabled");
seq_puts(s, "\n");
@ -924,6 +956,5 @@ static int __init pwm_debugfs_init(void)
return 0;
}
subsys_initcall(pwm_debugfs_init);
#endif /* CONFIG_DEBUG_FS */

View File

@ -217,6 +217,11 @@ static const struct atmel_hlcdc_pwm_errata atmel_hlcdc_pwm_sama5d3_errata = {
};
static const struct of_device_id atmel_hlcdc_dt_ids[] = {
{
.compatible = "atmel,at91sam9n12-hlcdc",
/* 9n12 has same errata as 9x5 HLCDC PWM */
.data = &atmel_hlcdc_pwm_at91sam9x5_errata,
},
{
.compatible = "atmel,at91sam9x5-hlcdc",
.data = &atmel_hlcdc_pwm_at91sam9x5_errata,

View File

@ -347,7 +347,7 @@ static int atmel_tcb_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
tcbpwm->duty = duty;
/* If the PWM is enabled, call enable to apply the new conf */
if (test_bit(PWMF_ENABLED, &pwm->flags))
if (pwm_is_enabled(pwm))
atmel_tcb_pwm_enable(chip, pwm);
return 0;

View File

@ -114,7 +114,7 @@ static int atmel_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
u32 val;
int ret;
if (test_bit(PWMF_ENABLED, &pwm->flags) && (period_ns != pwm->period)) {
if (pwm_is_enabled(pwm) && (period_ns != pwm_get_period(pwm))) {
dev_err(chip->dev, "cannot change PWM period while enabled\n");
return -EBUSY;
}
@ -176,7 +176,7 @@ static void atmel_pwm_config_v1(struct pwm_chip *chip, struct pwm_device *pwm,
* If the PWM channel is enabled, only update CDTY by using the update
* register, it needs to set bit 10 of CMR to 0
*/
if (test_bit(PWMF_ENABLED, &pwm->flags))
if (pwm_is_enabled(pwm))
return;
/*
* If the PWM channel is disabled, write value to duty and period
@ -191,7 +191,7 @@ static void atmel_pwm_config_v2(struct pwm_chip *chip, struct pwm_device *pwm,
{
struct atmel_pwm_chip *atmel_pwm = to_atmel_pwm_chip(chip);
if (test_bit(PWMF_ENABLED, &pwm->flags)) {
if (pwm_is_enabled(pwm)) {
/*
* If the PWM channel is enabled, using the duty update register
* to update the value.

View File

@ -76,19 +76,36 @@ static inline struct kona_pwmc *to_kona_pwmc(struct pwm_chip *_chip)
return container_of(_chip, struct kona_pwmc, chip);
}
static void kona_pwmc_apply_settings(struct kona_pwmc *kp, unsigned int chan)
/*
* Clear trigger bit but set smooth bit to maintain old output.
*/
static void kona_pwmc_prepare_for_settings(struct kona_pwmc *kp,
unsigned int chan)
{
unsigned int value = readl(kp->base + PWM_CONTROL_OFFSET);
/* Clear trigger bit but set smooth bit to maintain old output */
value |= 1 << PWM_CONTROL_SMOOTH_SHIFT(chan);
value &= ~(1 << PWM_CONTROL_TRIGGER_SHIFT(chan));
writel(value, kp->base + PWM_CONTROL_OFFSET);
/*
* There must be a min 400ns delay between clearing trigger and setting
* it. Failing to do this may result in no PWM signal.
*/
ndelay(400);
}
static void kona_pwmc_apply_settings(struct kona_pwmc *kp, unsigned int chan)
{
unsigned int value = readl(kp->base + PWM_CONTROL_OFFSET);
/* Set trigger bit and clear smooth bit to apply new settings */
value &= ~(1 << PWM_CONTROL_SMOOTH_SHIFT(chan));
value |= 1 << PWM_CONTROL_TRIGGER_SHIFT(chan);
writel(value, kp->base + PWM_CONTROL_OFFSET);
/* Trigger bit must be held high for at least 400 ns. */
ndelay(400);
}
static int kona_pwmc_config(struct pwm_chip *chip, struct pwm_device *pwm,
@ -133,8 +150,14 @@ static int kona_pwmc_config(struct pwm_chip *chip, struct pwm_device *pwm,
return -EINVAL;
}
/* If the PWM channel is enabled, write the settings to the HW */
if (test_bit(PWMF_ENABLED, &pwm->flags)) {
/*
* Don't apply settings if disabled. The period and duty cycle are
* always calculated above to ensure the new values are
* validated immediately instead of on enable.
*/
if (pwm_is_enabled(pwm)) {
kona_pwmc_prepare_for_settings(kp, chan);
value = readl(kp->base + PRESCALE_OFFSET);
value &= ~PRESCALE_MASK(chan);
value |= prescale << PRESCALE_SHIFT(chan);
@ -164,6 +187,8 @@ static int kona_pwmc_set_polarity(struct pwm_chip *chip, struct pwm_device *pwm,
return ret;
}
kona_pwmc_prepare_for_settings(kp, chan);
value = readl(kp->base + PWM_CONTROL_OFFSET);
if (polarity == PWM_POLARITY_NORMAL)
@ -175,9 +200,6 @@ static int kona_pwmc_set_polarity(struct pwm_chip *chip, struct pwm_device *pwm,
kona_pwmc_apply_settings(kp, chan);
/* Wait for waveform to settle before gating off the clock */
ndelay(400);
clk_disable_unprepare(kp->clk);
return 0;
@ -194,7 +216,8 @@ static int kona_pwmc_enable(struct pwm_chip *chip, struct pwm_device *pwm)
return ret;
}
ret = kona_pwmc_config(chip, pwm, pwm->duty_cycle, pwm->period);
ret = kona_pwmc_config(chip, pwm, pwm_get_duty_cycle(pwm),
pwm_get_period(pwm));
if (ret < 0) {
clk_disable_unprepare(kp->clk);
return ret;
@ -207,13 +230,20 @@ static void kona_pwmc_disable(struct pwm_chip *chip, struct pwm_device *pwm)
{
struct kona_pwmc *kp = to_kona_pwmc(chip);
unsigned int chan = pwm->hwpwm;
unsigned int value;
kona_pwmc_prepare_for_settings(kp, chan);
/* Simulate a disable by configuring for zero duty */
writel(0, kp->base + DUTY_CYCLE_HIGH_OFFSET(chan));
kona_pwmc_apply_settings(kp, chan);
writel(0, kp->base + PERIOD_COUNT_OFFSET(chan));
/* Wait for waveform to settle before gating off the clock */
ndelay(400);
/* Set prescale to 0 for this channel */
value = readl(kp->base + PRESCALE_OFFSET);
value &= ~PRESCALE_MASK(chan);
writel(value, kp->base + PRESCALE_OFFSET);
kona_pwmc_apply_settings(kp, chan);
clk_disable_unprepare(kp->clk);
}
@ -287,7 +317,7 @@ static int kona_pwmc_remove(struct platform_device *pdev)
unsigned int chan;
for (chan = 0; chan < kp->chip.npwm; chan++)
if (test_bit(PWMF_ENABLED, &kp->chip.pwms[chan].flags))
if (pwm_is_enabled(&kp->chip.pwms[chan]))
clk_disable_unprepare(kp->clk);
return pwmchip_remove(&kp->chip);

View File

@ -82,7 +82,7 @@ static int ep93xx_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
* The clock needs to be enabled to access the PWM registers.
* Configuration can be changed at any time.
*/
if (!test_bit(PWMF_ENABLED, &pwm->flags)) {
if (!pwm_is_enabled(pwm)) {
ret = clk_enable(ep93xx_pwm->clk);
if (ret)
return ret;
@ -113,7 +113,7 @@ static int ep93xx_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
ret = -EINVAL;
}
if (!test_bit(PWMF_ENABLED, &pwm->flags))
if (!pwm_is_enabled(pwm))
clk_disable(ep93xx_pwm->clk);
return ret;

View File

@ -114,7 +114,7 @@ static int imx_pwm_config_v2(struct pwm_chip *chip,
unsigned long long c;
unsigned long period_cycles, duty_cycles, prescale;
unsigned int period_ms;
bool enable = test_bit(PWMF_ENABLED, &pwm->flags);
bool enable = pwm_is_enabled(pwm);
int wait_count = 0, fifoav;
u32 cr, sr;
@ -129,7 +129,8 @@ static int imx_pwm_config_v2(struct pwm_chip *chip,
sr = readl(imx->mmio_base + MX3_PWMSR);
fifoav = sr & MX3_PWMSR_FIFOAV_MASK;
if (fifoav == MX3_PWMSR_FIFOAV_4WORDS) {
period_ms = DIV_ROUND_UP(pwm->period, NSEC_PER_MSEC);
period_ms = DIV_ROUND_UP(pwm_get_period(pwm),
NSEC_PER_MSEC);
msleep(period_ms);
sr = readl(imx->mmio_base + MX3_PWMSR);

View File

@ -0,0 +1,465 @@
/*
* NXP LPC18xx State Configurable Timer - Pulse Width Modulator driver
*
* Copyright (c) 2015 Ariel D'Alessandro <ariel@vanguardiasur.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License.
*
* Notes
* =====
* NXP LPC18xx provides a State Configurable Timer (SCT) which can be configured
* as a Pulse Width Modulator.
*
* SCT supports 16 outputs, 16 events and 16 registers. Each event will be
* triggered when its related register matches the SCT counter value, and it
* will set or clear a selected output.
*
* One of the events is preselected to generate the period, thus the maximum
* number of simultaneous channels is limited to 15. Notice that period is
* global to all the channels, thus PWM driver will refuse setting different
* values to it, unless there's only one channel requested.
*/
#include <linux/clk.h>
#include <linux/err.h>
#include <linux/io.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/pwm.h>
/* LPC18xx SCT registers */
#define LPC18XX_PWM_CONFIG 0x000
#define LPC18XX_PWM_CONFIG_UNIFY BIT(0)
#define LPC18XX_PWM_CONFIG_NORELOAD BIT(7)
#define LPC18XX_PWM_CTRL 0x004
#define LPC18XX_PWM_CTRL_HALT BIT(2)
#define LPC18XX_PWM_BIDIR BIT(4)
#define LPC18XX_PWM_PRE_SHIFT 5
#define LPC18XX_PWM_PRE_MASK (0xff << LPC18XX_PWM_PRE_SHIFT)
#define LPC18XX_PWM_PRE(x) (x << LPC18XX_PWM_PRE_SHIFT)
#define LPC18XX_PWM_LIMIT 0x008
#define LPC18XX_PWM_RES_BASE 0x058
#define LPC18XX_PWM_RES_SHIFT(_ch) (_ch * 2)
#define LPC18XX_PWM_RES(_ch, _action) (_action << LPC18XX_PWM_RES_SHIFT(_ch))
#define LPC18XX_PWM_RES_MASK(_ch) (0x3 << LPC18XX_PWM_RES_SHIFT(_ch))
#define LPC18XX_PWM_MATCH_BASE 0x100
#define LPC18XX_PWM_MATCH(_ch) (LPC18XX_PWM_MATCH_BASE + _ch * 4)
#define LPC18XX_PWM_MATCHREL_BASE 0x200
#define LPC18XX_PWM_MATCHREL(_ch) (LPC18XX_PWM_MATCHREL_BASE + _ch * 4)
#define LPC18XX_PWM_EVSTATEMSK_BASE 0x300
#define LPC18XX_PWM_EVSTATEMSK(_ch) (LPC18XX_PWM_EVSTATEMSK_BASE + _ch * 8)
#define LPC18XX_PWM_EVSTATEMSK_ALL 0xffffffff
#define LPC18XX_PWM_EVCTRL_BASE 0x304
#define LPC18XX_PWM_EVCTRL(_ev) (LPC18XX_PWM_EVCTRL_BASE + _ev * 8)
#define LPC18XX_PWM_EVCTRL_MATCH(_ch) _ch
#define LPC18XX_PWM_EVCTRL_COMB_SHIFT 12
#define LPC18XX_PWM_EVCTRL_COMB_MATCH (0x1 << LPC18XX_PWM_EVCTRL_COMB_SHIFT)
#define LPC18XX_PWM_OUTPUTSET_BASE 0x500
#define LPC18XX_PWM_OUTPUTSET(_ch) (LPC18XX_PWM_OUTPUTSET_BASE + _ch * 8)
#define LPC18XX_PWM_OUTPUTCL_BASE 0x504
#define LPC18XX_PWM_OUTPUTCL(_ch) (LPC18XX_PWM_OUTPUTCL_BASE + _ch * 8)
/* LPC18xx SCT unified counter */
#define LPC18XX_PWM_TIMER_MAX 0xffffffff
/* LPC18xx SCT events */
#define LPC18XX_PWM_EVENT_PERIOD 0
#define LPC18XX_PWM_EVENT_MAX 16
/* SCT conflict resolution */
enum lpc18xx_pwm_res_action {
LPC18XX_PWM_RES_NONE,
LPC18XX_PWM_RES_SET,
LPC18XX_PWM_RES_CLEAR,
LPC18XX_PWM_RES_TOGGLE,
};
struct lpc18xx_pwm_data {
unsigned int duty_event;
};
struct lpc18xx_pwm_chip {
struct device *dev;
struct pwm_chip chip;
void __iomem *base;
struct clk *pwm_clk;
unsigned long clk_rate;
unsigned int period_ns;
unsigned int min_period_ns;
unsigned int max_period_ns;
unsigned int period_event;
unsigned long event_map;
struct mutex res_lock;
struct mutex period_lock;
};
static inline struct lpc18xx_pwm_chip *
to_lpc18xx_pwm_chip(struct pwm_chip *chip)
{
return container_of(chip, struct lpc18xx_pwm_chip, chip);
}
static inline void lpc18xx_pwm_writel(struct lpc18xx_pwm_chip *lpc18xx_pwm,
u32 reg, u32 val)
{
writel(val, lpc18xx_pwm->base + reg);
}
static inline u32 lpc18xx_pwm_readl(struct lpc18xx_pwm_chip *lpc18xx_pwm,
u32 reg)
{
return readl(lpc18xx_pwm->base + reg);
}
static void lpc18xx_pwm_set_conflict_res(struct lpc18xx_pwm_chip *lpc18xx_pwm,
struct pwm_device *pwm,
enum lpc18xx_pwm_res_action action)
{
u32 val;
mutex_lock(&lpc18xx_pwm->res_lock);
/*
* Simultaneous set and clear may happen on an output, that is the case
* when duty_ns == period_ns. LPC18xx SCT allows to set a conflict
* resolution action to be taken in such a case.
*/
val = lpc18xx_pwm_readl(lpc18xx_pwm, LPC18XX_PWM_RES_BASE);
val &= ~LPC18XX_PWM_RES_MASK(pwm->hwpwm);
val |= LPC18XX_PWM_RES(pwm->hwpwm, action);
lpc18xx_pwm_writel(lpc18xx_pwm, LPC18XX_PWM_RES_BASE, val);
mutex_unlock(&lpc18xx_pwm->res_lock);
}
static void lpc18xx_pwm_config_period(struct pwm_chip *chip, int period_ns)
{
struct lpc18xx_pwm_chip *lpc18xx_pwm = to_lpc18xx_pwm_chip(chip);
u64 val;
val = (u64)period_ns * lpc18xx_pwm->clk_rate;
do_div(val, NSEC_PER_SEC);
lpc18xx_pwm_writel(lpc18xx_pwm,
LPC18XX_PWM_MATCH(lpc18xx_pwm->period_event),
(u32)val - 1);
lpc18xx_pwm_writel(lpc18xx_pwm,
LPC18XX_PWM_MATCHREL(lpc18xx_pwm->period_event),
(u32)val - 1);
}
static void lpc18xx_pwm_config_duty(struct pwm_chip *chip,
struct pwm_device *pwm, int duty_ns)
{
struct lpc18xx_pwm_chip *lpc18xx_pwm = to_lpc18xx_pwm_chip(chip);
struct lpc18xx_pwm_data *lpc18xx_data = pwm_get_chip_data(pwm);
u64 val;
val = (u64)duty_ns * lpc18xx_pwm->clk_rate;
do_div(val, NSEC_PER_SEC);
lpc18xx_pwm_writel(lpc18xx_pwm,
LPC18XX_PWM_MATCH(lpc18xx_data->duty_event),
(u32)val);
lpc18xx_pwm_writel(lpc18xx_pwm,
LPC18XX_PWM_MATCHREL(lpc18xx_data->duty_event),
(u32)val);
}
static int lpc18xx_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
int duty_ns, int period_ns)
{
struct lpc18xx_pwm_chip *lpc18xx_pwm = to_lpc18xx_pwm_chip(chip);
int requested_events, i;
if (period_ns < lpc18xx_pwm->min_period_ns ||
period_ns > lpc18xx_pwm->max_period_ns) {
dev_err(chip->dev, "period %d not in range\n", period_ns);
return -ERANGE;
}
mutex_lock(&lpc18xx_pwm->period_lock);
requested_events = bitmap_weight(&lpc18xx_pwm->event_map,
LPC18XX_PWM_EVENT_MAX);
/*
* The PWM supports only a single period for all PWM channels.
* Once the period is set, it can only be changed if no more than one
* channel is requested at that moment.
*/
if (requested_events > 2 && lpc18xx_pwm->period_ns != period_ns &&
lpc18xx_pwm->period_ns) {
dev_err(chip->dev, "conflicting period requested for PWM %u\n",
pwm->hwpwm);
mutex_unlock(&lpc18xx_pwm->period_lock);
return -EBUSY;
}
if ((requested_events <= 2 && lpc18xx_pwm->period_ns != period_ns) ||
!lpc18xx_pwm->period_ns) {
lpc18xx_pwm->period_ns = period_ns;
for (i = 0; i < chip->npwm; i++)
pwm_set_period(&chip->pwms[i], period_ns);
lpc18xx_pwm_config_period(chip, period_ns);
}
mutex_unlock(&lpc18xx_pwm->period_lock);
lpc18xx_pwm_config_duty(chip, pwm, duty_ns);
return 0;
}
static int lpc18xx_pwm_set_polarity(struct pwm_chip *chip,
struct pwm_device *pwm,
enum pwm_polarity polarity)
{
return 0;
}
static int lpc18xx_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm)
{
struct lpc18xx_pwm_chip *lpc18xx_pwm = to_lpc18xx_pwm_chip(chip);
struct lpc18xx_pwm_data *lpc18xx_data = pwm_get_chip_data(pwm);
enum lpc18xx_pwm_res_action res_action;
unsigned int set_event, clear_event;
lpc18xx_pwm_writel(lpc18xx_pwm,
LPC18XX_PWM_EVCTRL(lpc18xx_data->duty_event),
LPC18XX_PWM_EVCTRL_MATCH(lpc18xx_data->duty_event) |
LPC18XX_PWM_EVCTRL_COMB_MATCH);
lpc18xx_pwm_writel(lpc18xx_pwm,
LPC18XX_PWM_EVSTATEMSK(lpc18xx_data->duty_event),
LPC18XX_PWM_EVSTATEMSK_ALL);
if (pwm->polarity == PWM_POLARITY_NORMAL) {
set_event = lpc18xx_pwm->period_event;
clear_event = lpc18xx_data->duty_event;
res_action = LPC18XX_PWM_RES_SET;
} else {
set_event = lpc18xx_data->duty_event;
clear_event = lpc18xx_pwm->period_event;
res_action = LPC18XX_PWM_RES_CLEAR;
}
lpc18xx_pwm_writel(lpc18xx_pwm, LPC18XX_PWM_OUTPUTSET(pwm->hwpwm),
BIT(set_event));
lpc18xx_pwm_writel(lpc18xx_pwm, LPC18XX_PWM_OUTPUTCL(pwm->hwpwm),
BIT(clear_event));
lpc18xx_pwm_set_conflict_res(lpc18xx_pwm, pwm, res_action);
return 0;
}
static void lpc18xx_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm)
{
struct lpc18xx_pwm_chip *lpc18xx_pwm = to_lpc18xx_pwm_chip(chip);
struct lpc18xx_pwm_data *lpc18xx_data = pwm_get_chip_data(pwm);
lpc18xx_pwm_writel(lpc18xx_pwm,
LPC18XX_PWM_EVCTRL(lpc18xx_data->duty_event), 0);
lpc18xx_pwm_writel(lpc18xx_pwm, LPC18XX_PWM_OUTPUTSET(pwm->hwpwm), 0);
lpc18xx_pwm_writel(lpc18xx_pwm, LPC18XX_PWM_OUTPUTCL(pwm->hwpwm), 0);
}
static int lpc18xx_pwm_request(struct pwm_chip *chip, struct pwm_device *pwm)
{
struct lpc18xx_pwm_chip *lpc18xx_pwm = to_lpc18xx_pwm_chip(chip);
struct lpc18xx_pwm_data *lpc18xx_data = pwm_get_chip_data(pwm);
unsigned long event;
event = find_first_zero_bit(&lpc18xx_pwm->event_map,
LPC18XX_PWM_EVENT_MAX);
if (event >= LPC18XX_PWM_EVENT_MAX) {
dev_err(lpc18xx_pwm->dev,
"maximum number of simultaneous channels reached\n");
return -EBUSY;
};
set_bit(event, &lpc18xx_pwm->event_map);
lpc18xx_data->duty_event = event;
lpc18xx_pwm_config_duty(chip, pwm, pwm_get_duty_cycle(pwm));
return 0;
}
static void lpc18xx_pwm_free(struct pwm_chip *chip, struct pwm_device *pwm)
{
struct lpc18xx_pwm_chip *lpc18xx_pwm = to_lpc18xx_pwm_chip(chip);
struct lpc18xx_pwm_data *lpc18xx_data = pwm_get_chip_data(pwm);
pwm_disable(pwm);
pwm_set_duty_cycle(pwm, 0);
clear_bit(lpc18xx_data->duty_event, &lpc18xx_pwm->event_map);
}
static const struct pwm_ops lpc18xx_pwm_ops = {
.config = lpc18xx_pwm_config,
.set_polarity = lpc18xx_pwm_set_polarity,
.enable = lpc18xx_pwm_enable,
.disable = lpc18xx_pwm_disable,
.request = lpc18xx_pwm_request,
.free = lpc18xx_pwm_free,
.owner = THIS_MODULE,
};
static const struct of_device_id lpc18xx_pwm_of_match[] = {
{ .compatible = "nxp,lpc1850-sct-pwm" },
{}
};
MODULE_DEVICE_TABLE(of, lpc18xx_pwm_of_match);
static int lpc18xx_pwm_probe(struct platform_device *pdev)
{
struct lpc18xx_pwm_chip *lpc18xx_pwm;
struct pwm_device *pwm;
struct resource *res;
int ret, i;
u64 val;
lpc18xx_pwm = devm_kzalloc(&pdev->dev, sizeof(*lpc18xx_pwm),
GFP_KERNEL);
if (!lpc18xx_pwm)
return -ENOMEM;
lpc18xx_pwm->dev = &pdev->dev;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
lpc18xx_pwm->base = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(lpc18xx_pwm->base))
return PTR_ERR(lpc18xx_pwm->base);
lpc18xx_pwm->pwm_clk = devm_clk_get(&pdev->dev, "pwm");
if (IS_ERR(lpc18xx_pwm->pwm_clk)) {
dev_err(&pdev->dev, "failed to get pwm clock\n");
return PTR_ERR(lpc18xx_pwm->pwm_clk);
}
ret = clk_prepare_enable(lpc18xx_pwm->pwm_clk);
if (ret < 0) {
dev_err(&pdev->dev, "could not prepare or enable pwm clock\n");
return ret;
}
lpc18xx_pwm->clk_rate = clk_get_rate(lpc18xx_pwm->pwm_clk);
mutex_init(&lpc18xx_pwm->res_lock);
mutex_init(&lpc18xx_pwm->period_lock);
val = (u64)NSEC_PER_SEC * LPC18XX_PWM_TIMER_MAX;
do_div(val, lpc18xx_pwm->clk_rate);
lpc18xx_pwm->max_period_ns = val;
lpc18xx_pwm->min_period_ns = DIV_ROUND_UP(NSEC_PER_SEC,
lpc18xx_pwm->clk_rate);
lpc18xx_pwm->chip.dev = &pdev->dev;
lpc18xx_pwm->chip.ops = &lpc18xx_pwm_ops;
lpc18xx_pwm->chip.base = -1;
lpc18xx_pwm->chip.npwm = 16;
lpc18xx_pwm->chip.of_xlate = of_pwm_xlate_with_flags;
lpc18xx_pwm->chip.of_pwm_n_cells = 3;
/* SCT counter must be in unify (32 bit) mode */
lpc18xx_pwm_writel(lpc18xx_pwm, LPC18XX_PWM_CONFIG,
LPC18XX_PWM_CONFIG_UNIFY);
/*
* Everytime the timer counter reaches the period value, the related
* event will be triggered and the counter reset to 0.
*/
set_bit(LPC18XX_PWM_EVENT_PERIOD, &lpc18xx_pwm->event_map);
lpc18xx_pwm->period_event = LPC18XX_PWM_EVENT_PERIOD;
lpc18xx_pwm_writel(lpc18xx_pwm,
LPC18XX_PWM_EVSTATEMSK(lpc18xx_pwm->period_event),
LPC18XX_PWM_EVSTATEMSK_ALL);
val = LPC18XX_PWM_EVCTRL_MATCH(lpc18xx_pwm->period_event) |
LPC18XX_PWM_EVCTRL_COMB_MATCH;
lpc18xx_pwm_writel(lpc18xx_pwm,
LPC18XX_PWM_EVCTRL(lpc18xx_pwm->period_event), val);
lpc18xx_pwm_writel(lpc18xx_pwm, LPC18XX_PWM_LIMIT,
BIT(lpc18xx_pwm->period_event));
ret = pwmchip_add(&lpc18xx_pwm->chip);
if (ret < 0) {
dev_err(&pdev->dev, "pwmchip_add failed: %d\n", ret);
goto disable_pwmclk;
}
for (i = 0; i < lpc18xx_pwm->chip.npwm; i++) {
pwm = &lpc18xx_pwm->chip.pwms[i];
pwm->chip_data = devm_kzalloc(lpc18xx_pwm->dev,
sizeof(struct lpc18xx_pwm_data),
GFP_KERNEL);
if (!pwm->chip_data) {
ret = -ENOMEM;
goto remove_pwmchip;
}
}
platform_set_drvdata(pdev, lpc18xx_pwm);
val = lpc18xx_pwm_readl(lpc18xx_pwm, LPC18XX_PWM_CTRL);
val &= ~LPC18XX_PWM_BIDIR;
val &= ~LPC18XX_PWM_CTRL_HALT;
val &= ~LPC18XX_PWM_PRE_MASK;
val |= LPC18XX_PWM_PRE(0);
lpc18xx_pwm_writel(lpc18xx_pwm, LPC18XX_PWM_CTRL, val);
return 0;
remove_pwmchip:
pwmchip_remove(&lpc18xx_pwm->chip);
disable_pwmclk:
clk_disable_unprepare(lpc18xx_pwm->pwm_clk);
return ret;
}
static int lpc18xx_pwm_remove(struct platform_device *pdev)
{
struct lpc18xx_pwm_chip *lpc18xx_pwm = platform_get_drvdata(pdev);
u32 val;
val = lpc18xx_pwm_readl(lpc18xx_pwm, LPC18XX_PWM_CTRL);
lpc18xx_pwm_writel(lpc18xx_pwm, LPC18XX_PWM_CTRL,
val | LPC18XX_PWM_CTRL_HALT);
clk_disable_unprepare(lpc18xx_pwm->pwm_clk);
return pwmchip_remove(&lpc18xx_pwm->chip);
}
static struct platform_driver lpc18xx_pwm_driver = {
.driver = {
.name = "lpc18xx-sct-pwm",
.of_match_table = lpc18xx_pwm_of_match,
},
.probe = lpc18xx_pwm_probe,
.remove = lpc18xx_pwm_remove,
};
module_platform_driver(lpc18xx_pwm_driver);
MODULE_AUTHOR("Ariel D'Alessandro <ariel@vanguardiasur.com.ar>");
MODULE_DESCRIPTION("NXP LPC18xx PWM driver");
MODULE_LICENSE("GPL v2");

View File

@ -77,7 +77,7 @@ static int mxs_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
* If the PWM channel is disabled, make sure to turn on the clock
* before writing the register. Otherwise, keep it enabled.
*/
if (!test_bit(PWMF_ENABLED, &pwm->flags)) {
if (!pwm_is_enabled(pwm)) {
ret = clk_prepare_enable(mxs->clk);
if (ret)
return ret;
@ -92,7 +92,7 @@ static int mxs_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
/*
* If the PWM is not enabled, turn the clock off again to save power.
*/
if (!test_bit(PWMF_ENABLED, &pwm->flags))
if (!pwm_is_enabled(pwm))
clk_disable_unprepare(mxs->clk);
return 0;

View File

@ -2,6 +2,7 @@
* Driver for PCA9685 16-channel 12-bit PWM LED controller
*
* Copyright (C) 2013 Steffen Trumtrar <s.trumtrar@pengutronix.de>
* Copyright (C) 2015 Clemens Gruber <clemens.gruber@pqgruber.com>
*
* based on the pwm-twl-led.c driver
*
@ -24,6 +25,15 @@
#include <linux/pwm.h>
#include <linux/regmap.h>
#include <linux/slab.h>
#include <linux/delay.h>
/*
* Because the PCA9685 has only one prescaler per chip, changing the period of
* one channel affects the period of all 16 PWM outputs!
* However, the ratio between each configured duty cycle and the chip-wide
* period remains constant, because the OFF time is set in proportion to the
* counter range.
*/
#define PCA9685_MODE1 0x00
#define PCA9685_MODE2 0x01
@ -42,10 +52,18 @@
#define PCA9685_ALL_LED_OFF_H 0xFD
#define PCA9685_PRESCALE 0xFE
#define PCA9685_PRESCALE_MIN 0x03 /* => max. frequency of 1526 Hz */
#define PCA9685_PRESCALE_MAX 0xFF /* => min. frequency of 24 Hz */
#define PCA9685_COUNTER_RANGE 4096
#define PCA9685_DEFAULT_PERIOD 5000000 /* Default period_ns = 1/200 Hz */
#define PCA9685_OSC_CLOCK_MHZ 25 /* Internal oscillator with 25 MHz */
#define PCA9685_NUMREGS 0xFF
#define PCA9685_MAXCHAN 0x10
#define LED_FULL (1 << 4)
#define MODE1_RESTART (1 << 7)
#define MODE1_SLEEP (1 << 4)
#define MODE2_INVRT (1 << 4)
#define MODE2_OUTDRV (1 << 2)
@ -59,6 +77,8 @@ struct pca9685 {
struct pwm_chip chip;
struct regmap *regmap;
int active_cnt;
int duty_ns;
int period_ns;
};
static inline struct pca9685 *to_pca(struct pwm_chip *chip)
@ -72,6 +92,47 @@ static int pca9685_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
struct pca9685 *pca = to_pca(chip);
unsigned long long duty;
unsigned int reg;
int prescale;
if (period_ns != pca->period_ns) {
prescale = DIV_ROUND_CLOSEST(PCA9685_OSC_CLOCK_MHZ * period_ns,
PCA9685_COUNTER_RANGE * 1000) - 1;
if (prescale >= PCA9685_PRESCALE_MIN &&
prescale <= PCA9685_PRESCALE_MAX) {
/* Put chip into sleep mode */
regmap_update_bits(pca->regmap, PCA9685_MODE1,
MODE1_SLEEP, MODE1_SLEEP);
/* Change the chip-wide output frequency */
regmap_write(pca->regmap, PCA9685_PRESCALE, prescale);
/* Wake the chip up */
regmap_update_bits(pca->regmap, PCA9685_MODE1,
MODE1_SLEEP, 0x0);
/* Wait 500us for the oscillator to be back up */
udelay(500);
pca->period_ns = period_ns;
/*
* If the duty cycle did not change, restart PWM with
* the same duty cycle to period ratio and return.
*/
if (duty_ns == pca->duty_ns) {
regmap_update_bits(pca->regmap, PCA9685_MODE1,
MODE1_RESTART, 0x1);
return 0;
}
} else {
dev_err(chip->dev,
"prescaler not set: period out of bounds!\n");
return -EINVAL;
}
}
pca->duty_ns = duty_ns;
if (duty_ns < 1) {
if (pwm->hwpwm >= PCA9685_MAXCHAN)
@ -85,6 +146,22 @@ static int pca9685_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
}
if (duty_ns == period_ns) {
/* Clear both OFF registers */
if (pwm->hwpwm >= PCA9685_MAXCHAN)
reg = PCA9685_ALL_LED_OFF_L;
else
reg = LED_N_OFF_L(pwm->hwpwm);
regmap_write(pca->regmap, reg, 0x0);
if (pwm->hwpwm >= PCA9685_MAXCHAN)
reg = PCA9685_ALL_LED_OFF_H;
else
reg = LED_N_OFF_H(pwm->hwpwm);
regmap_write(pca->regmap, reg, 0x0);
/* Set the full ON bit */
if (pwm->hwpwm >= PCA9685_MAXCHAN)
reg = PCA9685_ALL_LED_ON_H;
else
@ -95,7 +172,7 @@ static int pca9685_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
return 0;
}
duty = 4096 * (unsigned long long)duty_ns;
duty = PCA9685_COUNTER_RANGE * (unsigned long long)duty_ns;
duty = DIV_ROUND_UP_ULL(duty, period_ns);
if (pwm->hwpwm >= PCA9685_MAXCHAN)
@ -112,6 +189,14 @@ static int pca9685_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
regmap_write(pca->regmap, reg, ((int)duty >> 8) & 0xf);
/* Clear the full ON bit, otherwise the set OFF time has no effect */
if (pwm->hwpwm >= PCA9685_MAXCHAN)
reg = PCA9685_ALL_LED_ON_H;
else
reg = LED_N_ON_H(pwm->hwpwm);
regmap_write(pca->regmap, reg, 0);
return 0;
}
@ -228,6 +313,8 @@ static int pca9685_pwm_probe(struct i2c_client *client,
ret);
return ret;
}
pca->duty_ns = 0;
pca->period_ns = PCA9685_DEFAULT_PERIOD;
i2c_set_clientdata(client, pca);
@ -285,7 +372,6 @@ MODULE_DEVICE_TABLE(of, pca9685_dt_ids);
static struct i2c_driver pca9685_i2c_driver = {
.driver = {
.name = "pca9685-pwm",
.owner = THIS_MODULE,
.of_match_table = pca9685_dt_ids,
},
.probe = pca9685_pwm_probe,

View File

@ -301,7 +301,7 @@ static int tpu_pwm_config(struct pwm_chip *chip, struct pwm_device *_pwm,
pwm->duty = duty;
/* If the channel is disabled we're done. */
if (!test_bit(PWMF_ENABLED, &_pwm->flags))
if (!pwm_is_enabled(_pwm))
return 0;
if (duty_only && pwm->timer_on) {

View File

@ -83,7 +83,7 @@ static void rockchip_pwm_set_enable_v2(struct pwm_chip *chip,
PWM_CONTINUOUS;
u32 val;
if (pwm->polarity == PWM_POLARITY_INVERSED)
if (pwm_get_polarity(pwm) == PWM_POLARITY_INVERSED)
enable_conf |= PWM_DUTY_NEGATIVE | PWM_INACTIVE_POSITIVE;
else
enable_conf |= PWM_DUTY_POSITIVE | PWM_INACTIVE_NEGATIVE;

View File

@ -112,7 +112,7 @@ static int tegra_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
* If the PWM channel is disabled, make sure to turn on the clock
* before writing the register. Otherwise, keep it enabled.
*/
if (!test_bit(PWMF_ENABLED, &pwm->flags)) {
if (!pwm_is_enabled(pwm)) {
err = clk_prepare_enable(pc->clk);
if (err < 0)
return err;
@ -124,7 +124,7 @@ static int tegra_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
/*
* If the PWM is not enabled, turn the clock off again to save power.
*/
if (!test_bit(PWMF_ENABLED, &pwm->flags))
if (!pwm_is_enabled(pwm))
clk_disable_unprepare(pc->clk);
return 0;
@ -214,7 +214,7 @@ static int tegra_pwm_remove(struct platform_device *pdev)
for (i = 0; i < NUM_PWM; i++) {
struct pwm_device *pwm = &pc->chip.pwms[i];
if (!test_bit(PWMF_ENABLED, &pwm->flags))
if (!pwm_is_enabled(pwm))
if (clk_prepare_enable(pc->clk) < 0)
continue;

View File

@ -97,7 +97,7 @@ static int ecap_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
writew(reg_val, pc->mmio_base + ECCTL2);
if (!test_bit(PWMF_ENABLED, &pwm->flags)) {
if (!pwm_is_enabled(pwm)) {
/* Update active registers if not running */
writel(duty_cycles, pc->mmio_base + CAP2);
writel(period_cycles, pc->mmio_base + CAP1);
@ -111,7 +111,7 @@ static int ecap_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
writel(period_cycles, pc->mmio_base + CAP3);
}
if (!test_bit(PWMF_ENABLED, &pwm->flags)) {
if (!pwm_is_enabled(pwm)) {
reg_val = readw(pc->mmio_base + ECCTL2);
/* Disable APWM mode to put APWM output Low */
reg_val &= ~ECCTL2_APWM_MODE;
@ -179,7 +179,7 @@ static void ecap_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm)
static void ecap_pwm_free(struct pwm_chip *chip, struct pwm_device *pwm)
{
if (test_bit(PWMF_ENABLED, &pwm->flags)) {
if (pwm_is_enabled(pwm)) {
dev_warn(chip->dev, "Removing PWM device without disabling\n");
pm_runtime_put_sync(chip->dev);
}
@ -306,7 +306,7 @@ static int ecap_pwm_suspend(struct device *dev)
ecap_pwm_save_context(pc);
/* Disable explicitly if PWM is running */
if (test_bit(PWMF_ENABLED, &pwm->flags))
if (pwm_is_enabled(pwm))
pm_runtime_put_sync(dev);
return 0;
@ -318,7 +318,7 @@ static int ecap_pwm_resume(struct device *dev)
struct pwm_device *pwm = pc->chip.pwms;
/* Enable explicitly if PWM was running */
if (test_bit(PWMF_ENABLED, &pwm->flags))
if (pwm_is_enabled(pwm))
pm_runtime_get_sync(dev);
ecap_pwm_restore_context(pc);

View File

@ -407,7 +407,7 @@ static void ehrpwm_pwm_free(struct pwm_chip *chip, struct pwm_device *pwm)
{
struct ehrpwm_pwm_chip *pc = to_ehrpwm_pwm_chip(chip);
if (test_bit(PWMF_ENABLED, &pwm->flags)) {
if (pwm_is_enabled(pwm)) {
dev_warn(chip->dev, "Removing PWM device without disabling\n");
pm_runtime_put_sync(chip->dev);
}
@ -565,7 +565,7 @@ static int ehrpwm_pwm_suspend(struct device *dev)
for (i = 0; i < pc->chip.npwm; i++) {
struct pwm_device *pwm = &pc->chip.pwms[i];
if (!test_bit(PWMF_ENABLED, &pwm->flags))
if (!pwm_is_enabled(pwm))
continue;
/* Disable explicitly if PWM is running */
@ -582,7 +582,7 @@ static int ehrpwm_pwm_resume(struct device *dev)
for (i = 0; i < pc->chip.npwm; i++) {
struct pwm_device *pwm = &pc->chip.pwms[i];
if (!test_bit(PWMF_ENABLED, &pwm->flags))
if (!pwm_is_enabled(pwm))
continue;
/* Enable explicitly if PWM was running */

View File

@ -46,7 +46,7 @@ static ssize_t pwm_period_show(struct device *child,
{
const struct pwm_device *pwm = child_to_pwm_device(child);
return sprintf(buf, "%u\n", pwm->period);
return sprintf(buf, "%u\n", pwm_get_period(pwm));
}
static ssize_t pwm_period_store(struct device *child,
@ -61,7 +61,7 @@ static ssize_t pwm_period_store(struct device *child,
if (ret)
return ret;
ret = pwm_config(pwm, pwm->duty_cycle, val);
ret = pwm_config(pwm, pwm_get_duty_cycle(pwm), val);
return ret ? : size;
}
@ -72,7 +72,7 @@ static ssize_t pwm_duty_cycle_show(struct device *child,
{
const struct pwm_device *pwm = child_to_pwm_device(child);
return sprintf(buf, "%u\n", pwm->duty_cycle);
return sprintf(buf, "%u\n", pwm_get_duty_cycle(pwm));
}
static ssize_t pwm_duty_cycle_store(struct device *child,
@ -87,7 +87,7 @@ static ssize_t pwm_duty_cycle_store(struct device *child,
if (ret)
return ret;
ret = pwm_config(pwm, val, pwm->period);
ret = pwm_config(pwm, val, pwm_get_period(pwm));
return ret ? : size;
}
@ -97,7 +97,7 @@ static ssize_t pwm_enable_show(struct device *child,
char *buf)
{
const struct pwm_device *pwm = child_to_pwm_device(child);
int enabled = test_bit(PWMF_ENABLED, &pwm->flags);
int enabled = pwm_is_enabled(pwm);
return sprintf(buf, "%d\n", enabled);
}
@ -133,8 +133,19 @@ static ssize_t pwm_polarity_show(struct device *child,
char *buf)
{
const struct pwm_device *pwm = child_to_pwm_device(child);
const char *polarity = "unknown";
return sprintf(buf, "%s\n", pwm->polarity ? "inversed" : "normal");
switch (pwm_get_polarity(pwm)) {
case PWM_POLARITY_NORMAL:
polarity = "normal";
break;
case PWM_POLARITY_INVERSED:
polarity = "inversed";
break;
}
return sprintf(buf, "%s\n", polarity);
}
static ssize_t pwm_polarity_store(struct device *child,
@ -301,9 +312,9 @@ static struct attribute *pwm_chip_attrs[] = {
ATTRIBUTE_GROUPS(pwm_chip);
static struct class pwm_class = {
.name = "pwm",
.owner = THIS_MODULE,
.dev_groups = pwm_chip_groups,
.name = "pwm",
.owner = THIS_MODULE,
.dev_groups = pwm_chip_groups,
};
static int pwmchip_sysfs_match(struct device *parent, const void *data)

View File

@ -79,26 +79,43 @@ enum {
PWMF_EXPORTED = 1 << 2,
};
/**
* struct pwm_device - PWM channel object
* @label: name of the PWM device
* @flags: flags associated with the PWM device
* @hwpwm: per-chip relative index of the PWM device
* @pwm: global index of the PWM device
* @chip: PWM chip providing this PWM device
* @chip_data: chip-private data associated with the PWM device
* @period: period of the PWM signal (in nanoseconds)
* @duty_cycle: duty cycle of the PWM signal (in nanoseconds)
* @polarity: polarity of the PWM signal
*/
struct pwm_device {
const char *label;
unsigned long flags;
unsigned int hwpwm;
unsigned int pwm;
struct pwm_chip *chip;
void *chip_data;
const char *label;
unsigned long flags;
unsigned int hwpwm;
unsigned int pwm;
struct pwm_chip *chip;
void *chip_data;
unsigned int period; /* in nanoseconds */
unsigned int duty_cycle; /* in nanoseconds */
enum pwm_polarity polarity;
unsigned int period;
unsigned int duty_cycle;
enum pwm_polarity polarity;
};
static inline bool pwm_is_enabled(const struct pwm_device *pwm)
{
return test_bit(PWMF_ENABLED, &pwm->flags);
}
static inline void pwm_set_period(struct pwm_device *pwm, unsigned int period)
{
if (pwm)
pwm->period = period;
}
static inline unsigned int pwm_get_period(struct pwm_device *pwm)
static inline unsigned int pwm_get_period(const struct pwm_device *pwm)
{
return pwm ? pwm->period : 0;
}
@ -109,7 +126,7 @@ static inline void pwm_set_duty_cycle(struct pwm_device *pwm, unsigned int duty)
pwm->duty_cycle = duty;
}
static inline unsigned int pwm_get_duty_cycle(struct pwm_device *pwm)
static inline unsigned int pwm_get_duty_cycle(const struct pwm_device *pwm)
{
return pwm ? pwm->duty_cycle : 0;
}
@ -119,6 +136,11 @@ static inline unsigned int pwm_get_duty_cycle(struct pwm_device *pwm)
*/
int pwm_set_polarity(struct pwm_device *pwm, enum pwm_polarity polarity);
static inline enum pwm_polarity pwm_get_polarity(const struct pwm_device *pwm)
{
return pwm ? pwm->polarity : PWM_POLARITY_NORMAL;
}
/**
* struct pwm_ops - PWM controller operations
* @request: optional hook for requesting a PWM
@ -131,25 +153,18 @@ int pwm_set_polarity(struct pwm_device *pwm, enum pwm_polarity polarity);
* @owner: helps prevent removal of modules exporting active PWMs
*/
struct pwm_ops {
int (*request)(struct pwm_chip *chip,
struct pwm_device *pwm);
void (*free)(struct pwm_chip *chip,
struct pwm_device *pwm);
int (*config)(struct pwm_chip *chip,
struct pwm_device *pwm,
int duty_ns, int period_ns);
int (*set_polarity)(struct pwm_chip *chip,
struct pwm_device *pwm,
enum pwm_polarity polarity);
int (*enable)(struct pwm_chip *chip,
struct pwm_device *pwm);
void (*disable)(struct pwm_chip *chip,
struct pwm_device *pwm);
int (*request)(struct pwm_chip *chip, struct pwm_device *pwm);
void (*free)(struct pwm_chip *chip, struct pwm_device *pwm);
int (*config)(struct pwm_chip *chip, struct pwm_device *pwm,
int duty_ns, int period_ns);
int (*set_polarity)(struct pwm_chip *chip, struct pwm_device *pwm,
enum pwm_polarity polarity);
int (*enable)(struct pwm_chip *chip, struct pwm_device *pwm);
void (*disable)(struct pwm_chip *chip, struct pwm_device *pwm);
#ifdef CONFIG_DEBUG_FS
void (*dbg_show)(struct pwm_chip *chip,
struct seq_file *s);
void (*dbg_show)(struct pwm_chip *chip, struct seq_file *s);
#endif
struct module *owner;
struct module *owner;
};
/**
@ -160,22 +175,24 @@ struct pwm_ops {
* @base: number of first PWM controlled by this chip
* @npwm: number of PWMs controlled by this chip
* @pwms: array of PWM devices allocated by the framework
* @of_xlate: request a PWM device given a device tree PWM specifier
* @of_pwm_n_cells: number of cells expected in the device tree PWM specifier
* @can_sleep: must be true if the .config(), .enable() or .disable()
* operations may sleep
*/
struct pwm_chip {
struct device *dev;
struct list_head list;
const struct pwm_ops *ops;
int base;
unsigned int npwm;
struct device *dev;
struct list_head list;
const struct pwm_ops *ops;
int base;
unsigned int npwm;
struct pwm_device *pwms;
struct pwm_device *pwms;
struct pwm_device * (*of_xlate)(struct pwm_chip *pc,
const struct of_phandle_args *args);
unsigned int of_pwm_n_cells;
bool can_sleep;
struct pwm_device * (*of_xlate)(struct pwm_chip *pc,
const struct of_phandle_args *args);
unsigned int of_pwm_n_cells;
bool can_sleep;
};
#if IS_ENABLED(CONFIG_PWM)