pwm: Changes for v3.15-rc1

The legacy HAVE_PWM Kconfig symbol is finally being retired. Thanks a
 lot to Sascha Hauer for doing that.
 
 Three new drivers are added: Freescale FTM, Cirrus Logic CLPS711X and
 Intel Low Power Subsystem.
 
 An assortment of fixes and cleanups rounds things off for this release
 cycle.
 -----BEGIN PGP SIGNATURE-----
 Version: GnuPG v2.0.22 (GNU/Linux)
 
 iQIcBAABAgAGBQJTPmXoAAoJEN0jrNd/PrOhSacQAKNpqWHpFdFuhqpO6dvmqYj3
 dvf6EDMnNaOS+TjbCvwP5awAiBhTbJRaTclP1lXXXOnzHvzeeYWhS2ESp4Yl8mRx
 GRHj5OxmquaVPY5HN+6guVyCrgq4R2sxPU1P2VoPhhomhvP2VuEBbD/ddudC3e2k
 /e9BuBhUB9eaur6d+vKX7Bnz09wf+ASobgIisjyyqSYysDgE82BAanX/knnLIyQL
 RKCsz75w14rIxU/f8EML8EMnWiGINYpP+M/NGtPvcNBBOX9DkdzBvSvcbm+gS6ma
 g2P+zsJgxhUpvvmzhqUumADUU8BWo/P1Y/6FQGRku6EmmJQQspTvDvOs1jCauouC
 5vUA41Jwh+4+AKeNWN28tDlh9i5kKYdzYP5SeRcM9mW1SI7AIFmg62lxdus7ZnBB
 e8UFd26kp/hZxXPdDVHtQi9y5Z5kn4axutVpbISuW5P9z1HF9bFOVHKQVlk7D6uz
 EqqiYLdW/MxrmBq+v35biwx6afk3zJ8Qas/MmVIVTcLcLDTFLPEm4EawwcRZo8F3
 Jh4p4IHxjEgLYcwVBNOe4ZBJg10fM1gmh18dDTyri759HE1mpi4/DwTGcv3iK4AU
 njv4Q+qBq9QkY2ktw3qCkTDcwiM9jm+FHfdyKXeR5+CjfOf61/CF+N1jBQ8ZMrb7
 XIRHle+mvL/RYpPDML/P
 =pbF+
 -----END PGP SIGNATURE-----

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

Pull pwm changes from Thierry Reding:
 "The legacy HAVE_PWM Kconfig symbol is finally being retired.  Thanks a
  lot to Sascha Hauer for doing that.

  Three new drivers are added: Freescale FTM, Cirrus Logic CLPS711X and
  Intel Low Power Subsystem.

  An assortment of fixes and cleanups rounds things off for this release
  cycle"

* tag 'pwm/for-3.15-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/thierry.reding/linux-pwm:
  pwm: pxa: Constify OF match table
  pwm: pxa: Fix typo "pwm" -> "PWM"
  Revert "pwm: pxa: Use of_match_ptr()"
  pwm: add support for Intel Low Power Subsystem PWM
  pwm: Add CLPS711X PWM support
  pwm: atmel: correct CDTY calculation
  pwm: atmel: Fix polarity handling
  Documentation: Add device tree bindings for Freescale FTM PWM.
  pwm: Add Freescale FTM PWM driver support
  pwm: pxa: Use of_match_ptr()
  pwm: samsung: Use SIMPLE_DEV_PM_OPS macro
  pwm: renesas-tpu: Add dependency on HAS_IOMEM
  pwm: Remove obsolete HAVE_PWM Kconfig symbol
This commit is contained in:
Linus Torvalds 2014-04-05 18:32:31 -07:00
commit 9712d3c377
14 changed files with 952 additions and 28 deletions

View File

@ -0,0 +1,16 @@
* Cirris Logic CLPS711X PWM controller
Required properties:
- compatible: Shall contain "cirrus,clps711x-pwm".
- reg: Physical base address and length of the controller's registers.
- clocks: phandle + clock specifier pair of the PWM reference clock.
- #pwm-cells: Should be 1. The cell specifies the index of the channel.
Example:
pwm: pwm@80000400 {
compatible = "cirrus,ep7312-pwm",
"cirrus,clps711x-pwm";
reg = <0x80000400 0x4>;
clocks = <&clks 8>;
#pwm-cells = <1>;
};

View File

@ -0,0 +1,35 @@
Freescale FlexTimer Module (FTM) PWM controller
Required properties:
- compatible: Should be "fsl,vf610-ftm-pwm".
- reg: Physical base address and length of the controller's registers
- #pwm-cells: Should be 3. See pwm.txt in this directory for a description of
the cells format.
- clock-names: Should include the following module clock source entries:
"ftm_sys" (module clock, also can be used as counter clock),
"ftm_ext" (external counter clock),
"ftm_fix" (fixed counter clock),
"ftm_cnt_clk_en" (external and fixed counter clock enable/disable).
- clocks: Must contain a phandle and clock specifier for each entry in
clock-names, please see clock/clock-bindings.txt for details of the property
values.
- pinctrl-names: Must contain a "default" entry.
- pinctrl-NNN: One property must exist for each entry in pinctrl-names.
See pinctrl/pinctrl-bindings.txt for details of the property values.
Example:
pwm0: pwm@40038000 {
compatible = "fsl,vf610-ftm-pwm";
reg = <0x40038000 0x1000>;
#pwm-cells = <3>;
clock-names = "ftm_sys", "ftm_ext",
"ftm_fix", "ftm_cnt_clk_en";
clocks = <&clks VF610_CLK_FTM0>,
<&clks VF610_CLK_FTM0_EXT_SEL>,
<&clks VF610_CLK_FTM0_FIX_SEL>,
<&clks VF610_CLK_FTM0_EXT_FIX_EN>;
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_pwm0_1>;
};

View File

@ -113,9 +113,6 @@ config ARM_DMA_IOMMU_ALIGNMENT
endif endif
config HAVE_PWM
bool
config MIGHT_HAVE_PCI config MIGHT_HAVE_PCI
bool bool
@ -633,7 +630,6 @@ config ARCH_LPC32XX
select CPU_ARM926T select CPU_ARM926T
select GENERIC_CLOCKEVENTS select GENERIC_CLOCKEVENTS
select HAVE_IDE select HAVE_IDE
select HAVE_PWM
select USE_OF select USE_OF
help help
Support for the NXP LPC32XX family of processors Support for the NXP LPC32XX family of processors

View File

@ -7,7 +7,6 @@ comment "Intel/Marvell Dev Platforms (sorted by hardware release time)"
config MACH_PXA3XX_DT config MACH_PXA3XX_DT
bool "Support PXA3xx platforms from device tree" bool "Support PXA3xx platforms from device tree"
select CPU_PXA300 select CPU_PXA300
select HAVE_PWM
select POWER_SUPPLY select POWER_SUPPLY
select PXA3xx select PXA3xx
select USE_OF select USE_OF
@ -23,12 +22,10 @@ config ARCH_LUBBOCK
config MACH_MAINSTONE config MACH_MAINSTONE
bool "Intel HCDDBBVA0 Development Platform (aka Mainstone)" bool "Intel HCDDBBVA0 Development Platform (aka Mainstone)"
select HAVE_PWM
select PXA27x select PXA27x
config MACH_ZYLONITE config MACH_ZYLONITE
bool bool
select HAVE_PWM
select PXA3xx select PXA3xx
config MACH_ZYLONITE300 config MACH_ZYLONITE300
@ -123,7 +120,6 @@ config MACH_CM_X300
bool "CompuLab CM-X300 modules" bool "CompuLab CM-X300 modules"
select CPU_PXA300 select CPU_PXA300
select CPU_PXA310 select CPU_PXA310
select HAVE_PWM
select PXA3xx select PXA3xx
config MACH_CAPC7117 config MACH_CAPC7117
@ -214,7 +210,6 @@ config TRIZEPS_PCMCIA
config MACH_LOGICPD_PXA270 config MACH_LOGICPD_PXA270
bool "LogicPD PXA270 Card Engine Development Platform" bool "LogicPD PXA270 Card Engine Development Platform"
select HAVE_PWM
select PXA27x select PXA27x
config MACH_PCM027 config MACH_PCM027
@ -225,7 +220,6 @@ config MACH_PCM027
config MACH_PCM990_BASEBOARD config MACH_PCM990_BASEBOARD
bool "PHYTEC PCM-990 development board" bool "PHYTEC PCM-990 development board"
depends on MACH_PCM027 depends on MACH_PCM027
select HAVE_PWM
choice choice
prompt "display on pcm990" prompt "display on pcm990"
@ -249,7 +243,6 @@ config MACH_COLIBRI
config MACH_COLIBRI_PXA270_INCOME config MACH_COLIBRI_PXA270_INCOME
bool "Income s.r.o. PXA270 SBC" bool "Income s.r.o. PXA270 SBC"
depends on MACH_COLIBRI depends on MACH_COLIBRI
select HAVE_PWM
select PXA27x select PXA27x
config MACH_COLIBRI300 config MACH_COLIBRI300
@ -278,7 +271,6 @@ comment "End-user Products (sorted by vendor name)"
config MACH_H4700 config MACH_H4700
bool "HP iPAQ hx4700" bool "HP iPAQ hx4700"
select HAVE_PWM
select IWMMXT select IWMMXT
select PXA27x select PXA27x
@ -292,14 +284,12 @@ config MACH_HIMALAYA
config MACH_MAGICIAN config MACH_MAGICIAN
bool "Enable HTC Magician Support" bool "Enable HTC Magician Support"
select HAVE_PWM
select IWMMXT select IWMMXT
select PXA27x select PXA27x
config MACH_MIOA701 config MACH_MIOA701
bool "Mitac Mio A701 Support" bool "Mitac Mio A701 Support"
select GPIO_SYSFS select GPIO_SYSFS
select HAVE_PWM
select IWMMXT select IWMMXT
select PXA27x select PXA27x
help help
@ -309,7 +299,6 @@ config MACH_MIOA701
config PXA_EZX config PXA_EZX
bool "Motorola EZX Platform" bool "Motorola EZX Platform"
select HAVE_PWM
select IWMMXT select IWMMXT
select PXA27x select PXA27x
@ -349,7 +338,6 @@ config MACH_MP900C
config ARCH_PXA_PALM config ARCH_PXA_PALM
bool "PXA based Palm PDAs" bool "PXA based Palm PDAs"
select HAVE_PWM
config MACH_PALM27X config MACH_PALM27X
bool bool
@ -447,7 +435,6 @@ config MACH_TREO680
config MACH_RAUMFELD_RC config MACH_RAUMFELD_RC
bool "Raumfeld Controller" bool "Raumfeld Controller"
select CPU_PXA300 select CPU_PXA300
select HAVE_PWM
select POWER_SUPPLY select POWER_SUPPLY
select PXA3xx select PXA3xx
@ -611,7 +598,6 @@ config MACH_E800
config MACH_ZIPIT2 config MACH_ZIPIT2
bool "Zipit Z2 Handheld" bool "Zipit Z2 Handheld"
select HAVE_PWM
select PXA27x select PXA27x
endmenu endmenu

View File

@ -156,7 +156,7 @@ config INPUT_MAX8925_ONKEY
config INPUT_MAX8997_HAPTIC config INPUT_MAX8997_HAPTIC
tristate "MAXIM MAX8997 haptic controller support" tristate "MAXIM MAX8997 haptic controller support"
depends on PWM && HAVE_PWM && MFD_MAX8997 depends on PWM && MFD_MAX8997
select INPUT_FF_MEMLESS select INPUT_FF_MEMLESS
help help
This option enables device driver support for the haptic controller This option enables device driver support for the haptic controller
@ -470,7 +470,7 @@ config INPUT_PCF8574
config INPUT_PWM_BEEPER config INPUT_PWM_BEEPER
tristate "PWM beeper support" tristate "PWM beeper support"
depends on PWM && HAVE_PWM depends on PWM
help help
Say Y here to get support for PWM based beeper devices. Say Y here to get support for PWM based beeper devices.

View File

@ -71,6 +71,15 @@ config PWM_BFIN
To compile this driver as a module, choose M here: the module To compile this driver as a module, choose M here: the module
will be called pwm-bfin. will be called pwm-bfin.
config PWM_CLPS711X
tristate "CLPS711X PWM support"
depends on ARCH_CLPS711X || COMPILE_TEST
help
Generic PWM framework driver for Cirrus Logic CLPS711X.
To compile this driver as a module, choose M here: the module
will be called pwm-clps711x.
config PWM_EP93XX config PWM_EP93XX
tristate "Cirrus Logic EP93xx PWM support" tristate "Cirrus Logic EP93xx PWM support"
depends on ARCH_EP93XX depends on ARCH_EP93XX
@ -80,6 +89,16 @@ config PWM_EP93XX
To compile this driver as a module, choose M here: the module To compile this driver as a module, choose M here: the module
will be called pwm-ep93xx. will be called pwm-ep93xx.
config PWM_FSL_FTM
tristate "Freescale FlexTimer Module (FTM) PWM support"
depends on OF
help
Generic FTM PWM framework driver for Freescale VF610 and
Layerscape LS-1 SoCs.
To compile this driver as a module, choose M here: the module
will be called pwm-fsl-ftm.
config PWM_IMX config PWM_IMX
tristate "i.MX PWM support" tristate "i.MX PWM support"
depends on ARCH_MXC depends on ARCH_MXC
@ -119,6 +138,16 @@ config PWM_LPC32XX
To compile this driver as a module, choose M here: the module To compile this driver as a module, choose M here: the module
will be called pwm-lpc32xx. will be called pwm-lpc32xx.
config PWM_LPSS
tristate "Intel LPSS PWM support"
depends on ACPI
help
Generic PWM framework driver for Intel Low Power Subsystem PWM
controller.
To compile this driver as a module, choose M here: the module
will be called pwm-lpss.
config PWM_MXS config PWM_MXS
tristate "Freescale MXS PWM support" tristate "Freescale MXS PWM support"
depends on ARCH_MXS && OF depends on ARCH_MXS && OF
@ -160,6 +189,7 @@ config PWM_PXA
config PWM_RENESAS_TPU config PWM_RENESAS_TPU
tristate "Renesas TPU PWM support" tristate "Renesas TPU PWM support"
depends on ARCH_SHMOBILE || COMPILE_TEST depends on ARCH_SHMOBILE || COMPILE_TEST
depends on HAS_IOMEM
help help
This driver exposes the Timer Pulse Unit (TPU) PWM controller found This driver exposes the Timer Pulse Unit (TPU) PWM controller found
in Renesas chips through the PWM API. in Renesas chips through the PWM API.

View File

@ -4,11 +4,14 @@ obj-$(CONFIG_PWM_AB8500) += pwm-ab8500.o
obj-$(CONFIG_PWM_ATMEL) += pwm-atmel.o obj-$(CONFIG_PWM_ATMEL) += pwm-atmel.o
obj-$(CONFIG_PWM_ATMEL_TCB) += pwm-atmel-tcb.o obj-$(CONFIG_PWM_ATMEL_TCB) += pwm-atmel-tcb.o
obj-$(CONFIG_PWM_BFIN) += pwm-bfin.o obj-$(CONFIG_PWM_BFIN) += pwm-bfin.o
obj-$(CONFIG_PWM_CLPS711X) += pwm-clps711x.o
obj-$(CONFIG_PWM_EP93XX) += pwm-ep93xx.o obj-$(CONFIG_PWM_EP93XX) += pwm-ep93xx.o
obj-$(CONFIG_PWM_FSL_FTM) += pwm-fsl-ftm.o
obj-$(CONFIG_PWM_IMX) += pwm-imx.o obj-$(CONFIG_PWM_IMX) += pwm-imx.o
obj-$(CONFIG_PWM_JZ4740) += pwm-jz4740.o obj-$(CONFIG_PWM_JZ4740) += pwm-jz4740.o
obj-$(CONFIG_PWM_LP3943) += pwm-lp3943.o obj-$(CONFIG_PWM_LP3943) += pwm-lp3943.o
obj-$(CONFIG_PWM_LPC32XX) += pwm-lpc32xx.o obj-$(CONFIG_PWM_LPC32XX) += pwm-lpc32xx.o
obj-$(CONFIG_PWM_LPSS) += pwm-lpss.o
obj-$(CONFIG_PWM_MXS) += pwm-mxs.o obj-$(CONFIG_PWM_MXS) += pwm-mxs.o
obj-$(CONFIG_PWM_PCA9685) += pwm-pca9685.o obj-$(CONFIG_PWM_PCA9685) += pwm-pca9685.o
obj-$(CONFIG_PWM_PUV3) += pwm-puv3.o obj-$(CONFIG_PWM_PUV3) += pwm-puv3.o

View File

@ -32,6 +32,7 @@
/* Bit field in CMR */ /* Bit field in CMR */
#define PWM_CMR_CPOL (1 << 9) #define PWM_CMR_CPOL (1 << 9)
#define PWM_CMR_UPD_CDTY (1 << 10) #define PWM_CMR_UPD_CDTY (1 << 10)
#define PWM_CMR_CPRE_MSK 0xF
/* The following registers for PWM v1 */ /* The following registers for PWM v1 */
#define PWMV1_CDTY 0x04 #define PWMV1_CDTY 0x04
@ -104,6 +105,7 @@ static int atmel_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
unsigned long clk_rate, prd, dty; unsigned long clk_rate, prd, dty;
unsigned long long div; unsigned long long div;
unsigned int pres = 0; unsigned int pres = 0;
u32 val;
int ret; int ret;
if (test_bit(PWMF_ENABLED, &pwm->flags) && (period_ns != pwm->period)) { if (test_bit(PWMF_ENABLED, &pwm->flags) && (period_ns != pwm->period)) {
@ -131,7 +133,7 @@ static int atmel_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
prd = div; prd = div;
div *= duty_ns; div *= duty_ns;
do_div(div, period_ns); do_div(div, period_ns);
dty = div; dty = prd - div;
ret = clk_enable(atmel_pwm->clk); ret = clk_enable(atmel_pwm->clk);
if (ret) { if (ret) {
@ -139,7 +141,10 @@ static int atmel_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
return ret; return ret;
} }
atmel_pwm_ch_writel(atmel_pwm, pwm->hwpwm, PWM_CMR, pres); /* It is necessary to preserve CPOL, inside CMR */
val = atmel_pwm_ch_readl(atmel_pwm, pwm->hwpwm, PWM_CMR);
val = (val & ~PWM_CMR_CPRE_MSK) | (pres & PWM_CMR_CPRE_MSK);
atmel_pwm_ch_writel(atmel_pwm, pwm->hwpwm, PWM_CMR, val);
atmel_pwm->config(chip, pwm, dty, prd); atmel_pwm->config(chip, pwm, dty, prd);
clk_disable(atmel_pwm->clk); clk_disable(atmel_pwm->clk);

176
drivers/pwm/pwm-clps711x.c Normal file
View File

@ -0,0 +1,176 @@
/*
* Cirrus Logic CLPS711X PWM driver
*
* Copyright (C) 2014 Alexander Shiyan <shc_work@mail.ru>
*
* 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, or
* (at your option) any later version.
*/
#include <linux/clk.h>
#include <linux/io.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/pwm.h>
struct clps711x_chip {
struct pwm_chip chip;
void __iomem *pmpcon;
struct clk *clk;
spinlock_t lock;
};
static inline struct clps711x_chip *to_clps711x_chip(struct pwm_chip *chip)
{
return container_of(chip, struct clps711x_chip, chip);
}
static void clps711x_pwm_update_val(struct clps711x_chip *priv, u32 n, u32 v)
{
/* PWM0 - bits 4..7, PWM1 - bits 8..11 */
u32 shift = (n + 1) * 4;
unsigned long flags;
u32 tmp;
spin_lock_irqsave(&priv->lock, flags);
tmp = readl(priv->pmpcon);
tmp &= ~(0xf << shift);
tmp |= v << shift;
writel(tmp, priv->pmpcon);
spin_unlock_irqrestore(&priv->lock, flags);
}
static unsigned int clps711x_get_duty(struct pwm_device *pwm, unsigned int v)
{
/* Duty cycle 0..15 max */
return DIV_ROUND_CLOSEST(v * 0xf, pwm_get_period(pwm));
}
static int clps711x_pwm_request(struct pwm_chip *chip, struct pwm_device *pwm)
{
struct clps711x_chip *priv = to_clps711x_chip(chip);
unsigned int freq = clk_get_rate(priv->clk);
if (!freq)
return -EINVAL;
/* Store constant period value */
pwm_set_period(pwm, DIV_ROUND_CLOSEST(NSEC_PER_SEC, freq));
return 0;
}
static int clps711x_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
int duty_ns, int period_ns)
{
struct clps711x_chip *priv = to_clps711x_chip(chip);
unsigned int duty;
if (period_ns != pwm_get_period(pwm))
return -EINVAL;
duty = clps711x_get_duty(pwm, duty_ns);
clps711x_pwm_update_val(priv, pwm->hwpwm, duty);
return 0;
}
static int clps711x_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm)
{
struct clps711x_chip *priv = to_clps711x_chip(chip);
unsigned int duty;
duty = clps711x_get_duty(pwm, pwm_get_duty_cycle(pwm));
clps711x_pwm_update_val(priv, pwm->hwpwm, duty);
return 0;
}
static void clps711x_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm)
{
struct clps711x_chip *priv = to_clps711x_chip(chip);
clps711x_pwm_update_val(priv, pwm->hwpwm, 0);
}
static const struct pwm_ops clps711x_pwm_ops = {
.request = clps711x_pwm_request,
.config = clps711x_pwm_config,
.enable = clps711x_pwm_enable,
.disable = clps711x_pwm_disable,
.owner = THIS_MODULE,
};
static struct pwm_device *clps711x_pwm_xlate(struct pwm_chip *chip,
const struct of_phandle_args *args)
{
if (args->args[0] >= chip->npwm)
return ERR_PTR(-EINVAL);
return pwm_request_from_chip(chip, args->args[0], NULL);
}
static int clps711x_pwm_probe(struct platform_device *pdev)
{
struct clps711x_chip *priv;
struct resource *res;
priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
priv->pmpcon = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(priv->pmpcon))
return PTR_ERR(priv->pmpcon);
priv->clk = devm_clk_get(&pdev->dev, NULL);
if (IS_ERR(priv->clk))
return PTR_ERR(priv->clk);
priv->chip.ops = &clps711x_pwm_ops;
priv->chip.dev = &pdev->dev;
priv->chip.base = -1;
priv->chip.npwm = 2;
priv->chip.of_xlate = clps711x_pwm_xlate;
priv->chip.of_pwm_n_cells = 1;
spin_lock_init(&priv->lock);
platform_set_drvdata(pdev, priv);
return pwmchip_add(&priv->chip);
}
static int clps711x_pwm_remove(struct platform_device *pdev)
{
struct clps711x_chip *priv = platform_get_drvdata(pdev);
return pwmchip_remove(&priv->chip);
}
static const struct of_device_id __maybe_unused clps711x_pwm_dt_ids[] = {
{ .compatible = "cirrus,clps711x-pwm", },
{ }
};
MODULE_DEVICE_TABLE(of, clps711x_pwm_dt_ids);
static struct platform_driver clps711x_pwm_driver = {
.driver = {
.name = "clps711x-pwm",
.owner = THIS_MODULE,
.of_match_table = of_match_ptr(clps711x_pwm_dt_ids),
},
.probe = clps711x_pwm_probe,
.remove = clps711x_pwm_remove,
};
module_platform_driver(clps711x_pwm_driver);
MODULE_AUTHOR("Alexander Shiyan <shc_work@mail.ru>");
MODULE_DESCRIPTION("Cirrus Logic CLPS711X PWM driver");
MODULE_LICENSE("GPL");

495
drivers/pwm/pwm-fsl-ftm.c Normal file
View File

@ -0,0 +1,495 @@
/*
* Freescale FlexTimer Module (FTM) PWM Driver
*
* Copyright 2012-2013 Freescale Semiconductor, Inc.
*
* 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, or
* (at your option) any later version.
*/
#include <linux/clk.h>
#include <linux/err.h>
#include <linux/io.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/of_address.h>
#include <linux/platform_device.h>
#include <linux/pwm.h>
#include <linux/slab.h>
#define FTM_SC 0x00
#define FTM_SC_CLK_MASK 0x3
#define FTM_SC_CLK_SHIFT 3
#define FTM_SC_CLK(c) (((c) + 1) << FTM_SC_CLK_SHIFT)
#define FTM_SC_PS_MASK 0x7
#define FTM_SC_PS_SHIFT 0
#define FTM_CNT 0x04
#define FTM_MOD 0x08
#define FTM_CSC_BASE 0x0C
#define FTM_CSC_MSB BIT(5)
#define FTM_CSC_MSA BIT(4)
#define FTM_CSC_ELSB BIT(3)
#define FTM_CSC_ELSA BIT(2)
#define FTM_CSC(_channel) (FTM_CSC_BASE + ((_channel) * 8))
#define FTM_CV_BASE 0x10
#define FTM_CV(_channel) (FTM_CV_BASE + ((_channel) * 8))
#define FTM_CNTIN 0x4C
#define FTM_STATUS 0x50
#define FTM_MODE 0x54
#define FTM_MODE_FTMEN BIT(0)
#define FTM_MODE_INIT BIT(2)
#define FTM_MODE_PWMSYNC BIT(3)
#define FTM_SYNC 0x58
#define FTM_OUTINIT 0x5C
#define FTM_OUTMASK 0x60
#define FTM_COMBINE 0x64
#define FTM_DEADTIME 0x68
#define FTM_EXTTRIG 0x6C
#define FTM_POL 0x70
#define FTM_FMS 0x74
#define FTM_FILTER 0x78
#define FTM_FLTCTRL 0x7C
#define FTM_QDCTRL 0x80
#define FTM_CONF 0x84
#define FTM_FLTPOL 0x88
#define FTM_SYNCONF 0x8C
#define FTM_INVCTRL 0x90
#define FTM_SWOCTRL 0x94
#define FTM_PWMLOAD 0x98
enum fsl_pwm_clk {
FSL_PWM_CLK_SYS,
FSL_PWM_CLK_FIX,
FSL_PWM_CLK_EXT,
FSL_PWM_CLK_CNTEN,
FSL_PWM_CLK_MAX
};
struct fsl_pwm_chip {
struct pwm_chip chip;
struct mutex lock;
unsigned int use_count;
unsigned int cnt_select;
unsigned int clk_ps;
void __iomem *base;
int period_ns;
struct clk *clk[FSL_PWM_CLK_MAX];
};
static inline struct fsl_pwm_chip *to_fsl_chip(struct pwm_chip *chip)
{
return container_of(chip, struct fsl_pwm_chip, chip);
}
static int fsl_pwm_request(struct pwm_chip *chip, struct pwm_device *pwm)
{
struct fsl_pwm_chip *fpc = to_fsl_chip(chip);
return clk_prepare_enable(fpc->clk[FSL_PWM_CLK_SYS]);
}
static void fsl_pwm_free(struct pwm_chip *chip, struct pwm_device *pwm)
{
struct fsl_pwm_chip *fpc = to_fsl_chip(chip);
clk_disable_unprepare(fpc->clk[FSL_PWM_CLK_SYS]);
}
static int fsl_pwm_calculate_default_ps(struct fsl_pwm_chip *fpc,
enum fsl_pwm_clk index)
{
unsigned long sys_rate, cnt_rate;
unsigned long long ratio;
sys_rate = clk_get_rate(fpc->clk[FSL_PWM_CLK_SYS]);
if (!sys_rate)
return -EINVAL;
cnt_rate = clk_get_rate(fpc->clk[fpc->cnt_select]);
if (!cnt_rate)
return -EINVAL;
switch (index) {
case FSL_PWM_CLK_SYS:
fpc->clk_ps = 1;
break;
case FSL_PWM_CLK_FIX:
ratio = 2 * cnt_rate - 1;
do_div(ratio, sys_rate);
fpc->clk_ps = ratio;
break;
case FSL_PWM_CLK_EXT:
ratio = 4 * cnt_rate - 1;
do_div(ratio, sys_rate);
fpc->clk_ps = ratio;
break;
default:
return -EINVAL;
}
return 0;
}
static unsigned long fsl_pwm_calculate_cycles(struct fsl_pwm_chip *fpc,
unsigned long period_ns)
{
unsigned long long c, c0;
c = clk_get_rate(fpc->clk[fpc->cnt_select]);
c = c * period_ns;
do_div(c, 1000000000UL);
do {
c0 = c;
do_div(c0, (1 << fpc->clk_ps));
if (c0 <= 0xFFFF)
return (unsigned long)c0;
} while (++fpc->clk_ps < 8);
return 0;
}
static unsigned long fsl_pwm_calculate_period_cycles(struct fsl_pwm_chip *fpc,
unsigned long period_ns,
enum fsl_pwm_clk index)
{
int ret;
ret = fsl_pwm_calculate_default_ps(fpc, index);
if (ret) {
dev_err(fpc->chip.dev,
"failed to calculate default prescaler: %d\n",
ret);
return 0;
}
return fsl_pwm_calculate_cycles(fpc, period_ns);
}
static unsigned long fsl_pwm_calculate_period(struct fsl_pwm_chip *fpc,
unsigned long period_ns)
{
enum fsl_pwm_clk m0, m1;
unsigned long fix_rate, ext_rate, cycles;
cycles = fsl_pwm_calculate_period_cycles(fpc, period_ns,
FSL_PWM_CLK_SYS);
if (cycles) {
fpc->cnt_select = FSL_PWM_CLK_SYS;
return cycles;
}
fix_rate = clk_get_rate(fpc->clk[FSL_PWM_CLK_FIX]);
ext_rate = clk_get_rate(fpc->clk[FSL_PWM_CLK_EXT]);
if (fix_rate > ext_rate) {
m0 = FSL_PWM_CLK_FIX;
m1 = FSL_PWM_CLK_EXT;
} else {
m0 = FSL_PWM_CLK_EXT;
m1 = FSL_PWM_CLK_FIX;
}
cycles = fsl_pwm_calculate_period_cycles(fpc, period_ns, m0);
if (cycles) {
fpc->cnt_select = m0;
return cycles;
}
fpc->cnt_select = m1;
return fsl_pwm_calculate_period_cycles(fpc, period_ns, m1);
}
static unsigned long fsl_pwm_calculate_duty(struct fsl_pwm_chip *fpc,
unsigned long period_ns,
unsigned long duty_ns)
{
unsigned long long val, duty;
val = readl(fpc->base + FTM_MOD);
duty = duty_ns * (val + 1);
do_div(duty, period_ns);
return (unsigned long)duty;
}
static int fsl_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
int duty_ns, int period_ns)
{
struct fsl_pwm_chip *fpc = to_fsl_chip(chip);
u32 val, period, duty;
mutex_lock(&fpc->lock);
/*
* The Freescale FTM controller supports only a single period for
* all PWM channels, therefore incompatible changes need to be
* refused.
*/
if (fpc->period_ns && fpc->period_ns != period_ns) {
dev_err(fpc->chip.dev,
"conflicting period requested for PWM %u\n",
pwm->hwpwm);
mutex_unlock(&fpc->lock);
return -EBUSY;
}
if (!fpc->period_ns && duty_ns) {
period = fsl_pwm_calculate_period(fpc, period_ns);
if (!period) {
dev_err(fpc->chip.dev, "failed to calculate period\n");
mutex_unlock(&fpc->lock);
return -EINVAL;
}
val = readl(fpc->base + FTM_SC);
val &= ~(FTM_SC_PS_MASK << FTM_SC_PS_SHIFT);
val |= fpc->clk_ps;
writel(val, fpc->base + FTM_SC);
writel(period - 1, fpc->base + FTM_MOD);
fpc->period_ns = period_ns;
}
mutex_unlock(&fpc->lock);
duty = fsl_pwm_calculate_duty(fpc, period_ns, duty_ns);
writel(FTM_CSC_MSB | FTM_CSC_ELSB, fpc->base + FTM_CSC(pwm->hwpwm));
writel(duty, fpc->base + FTM_CV(pwm->hwpwm));
return 0;
}
static int fsl_pwm_set_polarity(struct pwm_chip *chip,
struct pwm_device *pwm,
enum pwm_polarity polarity)
{
struct fsl_pwm_chip *fpc = to_fsl_chip(chip);
u32 val;
val = readl(fpc->base + FTM_POL);
if (polarity == PWM_POLARITY_INVERSED)
val |= BIT(pwm->hwpwm);
else
val &= ~BIT(pwm->hwpwm);
writel(val, fpc->base + FTM_POL);
return 0;
}
static int fsl_counter_clock_enable(struct fsl_pwm_chip *fpc)
{
u32 val;
int ret;
if (fpc->use_count != 0)
return 0;
/* select counter clock source */
val = readl(fpc->base + FTM_SC);
val &= ~(FTM_SC_CLK_MASK << FTM_SC_CLK_SHIFT);
val |= FTM_SC_CLK(fpc->cnt_select);
writel(val, fpc->base + FTM_SC);
ret = clk_prepare_enable(fpc->clk[fpc->cnt_select]);
if (ret)
return ret;
ret = clk_prepare_enable(fpc->clk[FSL_PWM_CLK_CNTEN]);
if (ret) {
clk_disable_unprepare(fpc->clk[fpc->cnt_select]);
return ret;
}
fpc->use_count++;
return 0;
}
static int fsl_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm)
{
struct fsl_pwm_chip *fpc = to_fsl_chip(chip);
u32 val;
int ret;
mutex_lock(&fpc->lock);
val = readl(fpc->base + FTM_OUTMASK);
val &= ~BIT(pwm->hwpwm);
writel(val, fpc->base + FTM_OUTMASK);
ret = fsl_counter_clock_enable(fpc);
mutex_unlock(&fpc->lock);
return ret;
}
static void fsl_counter_clock_disable(struct fsl_pwm_chip *fpc)
{
u32 val;
/*
* already disabled, do nothing
*/
if (fpc->use_count == 0)
return;
/* there are still users, so can't disable yet */
if (--fpc->use_count > 0)
return;
/* no users left, disable PWM counter clock */
val = readl(fpc->base + FTM_SC);
val &= ~(FTM_SC_CLK_MASK << FTM_SC_CLK_SHIFT);
writel(val, fpc->base + FTM_SC);
clk_disable_unprepare(fpc->clk[FSL_PWM_CLK_CNTEN]);
clk_disable_unprepare(fpc->clk[fpc->cnt_select]);
}
static void fsl_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm)
{
struct fsl_pwm_chip *fpc = to_fsl_chip(chip);
u32 val;
mutex_lock(&fpc->lock);
val = readl(fpc->base + FTM_OUTMASK);
val |= BIT(pwm->hwpwm);
writel(val, fpc->base + FTM_OUTMASK);
fsl_counter_clock_disable(fpc);
val = readl(fpc->base + FTM_OUTMASK);
if ((val & 0xFF) == 0xFF)
fpc->period_ns = 0;
mutex_unlock(&fpc->lock);
}
static const struct pwm_ops fsl_pwm_ops = {
.request = fsl_pwm_request,
.free = fsl_pwm_free,
.config = fsl_pwm_config,
.set_polarity = fsl_pwm_set_polarity,
.enable = fsl_pwm_enable,
.disable = fsl_pwm_disable,
.owner = THIS_MODULE,
};
static int fsl_pwm_init(struct fsl_pwm_chip *fpc)
{
int ret;
ret = clk_prepare_enable(fpc->clk[FSL_PWM_CLK_SYS]);
if (ret)
return ret;
writel(0x00, fpc->base + FTM_CNTIN);
writel(0x00, fpc->base + FTM_OUTINIT);
writel(0xFF, fpc->base + FTM_OUTMASK);
clk_disable_unprepare(fpc->clk[FSL_PWM_CLK_SYS]);
return 0;
}
static int fsl_pwm_probe(struct platform_device *pdev)
{
struct fsl_pwm_chip *fpc;
struct resource *res;
int ret;
fpc = devm_kzalloc(&pdev->dev, sizeof(*fpc), GFP_KERNEL);
if (!fpc)
return -ENOMEM;
mutex_init(&fpc->lock);
fpc->chip.dev = &pdev->dev;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
fpc->base = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(fpc->base))
return PTR_ERR(fpc->base);
fpc->clk[FSL_PWM_CLK_SYS] = devm_clk_get(&pdev->dev, "ftm_sys");
if (IS_ERR(fpc->clk[FSL_PWM_CLK_SYS])) {
dev_err(&pdev->dev, "failed to get \"ftm_sys\" clock\n");
return PTR_ERR(fpc->clk[FSL_PWM_CLK_SYS]);
}
fpc->clk[FSL_PWM_CLK_FIX] = devm_clk_get(fpc->chip.dev, "ftm_fix");
if (IS_ERR(fpc->clk[FSL_PWM_CLK_FIX]))
return PTR_ERR(fpc->clk[FSL_PWM_CLK_FIX]);
fpc->clk[FSL_PWM_CLK_EXT] = devm_clk_get(fpc->chip.dev, "ftm_ext");
if (IS_ERR(fpc->clk[FSL_PWM_CLK_EXT]))
return PTR_ERR(fpc->clk[FSL_PWM_CLK_EXT]);
fpc->clk[FSL_PWM_CLK_CNTEN] =
devm_clk_get(fpc->chip.dev, "ftm_cnt_clk_en");
if (IS_ERR(fpc->clk[FSL_PWM_CLK_CNTEN]))
return PTR_ERR(fpc->clk[FSL_PWM_CLK_CNTEN]);
fpc->chip.ops = &fsl_pwm_ops;
fpc->chip.of_xlate = of_pwm_xlate_with_flags;
fpc->chip.of_pwm_n_cells = 3;
fpc->chip.base = -1;
fpc->chip.npwm = 8;
ret = pwmchip_add(&fpc->chip);
if (ret < 0) {
dev_err(&pdev->dev, "failed to add PWM chip: %d\n", ret);
return ret;
}
platform_set_drvdata(pdev, fpc);
return fsl_pwm_init(fpc);
}
static int fsl_pwm_remove(struct platform_device *pdev)
{
struct fsl_pwm_chip *fpc = platform_get_drvdata(pdev);
return pwmchip_remove(&fpc->chip);
}
static const struct of_device_id fsl_pwm_dt_ids[] = {
{ .compatible = "fsl,vf610-ftm-pwm", },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, fsl_pwm_dt_ids);
static struct platform_driver fsl_pwm_driver = {
.driver = {
.name = "fsl-ftm-pwm",
.of_match_table = fsl_pwm_dt_ids,
},
.probe = fsl_pwm_probe,
.remove = fsl_pwm_remove,
};
module_platform_driver(fsl_pwm_driver);
MODULE_DESCRIPTION("Freescale FlexTimer Module PWM Driver");
MODULE_AUTHOR("Xiubo Li <Li.Xiubo@freescale.com>");
MODULE_ALIAS("platform:fsl-ftm-pwm");
MODULE_LICENSE("GPL");

183
drivers/pwm/pwm-lpss.c Normal file
View File

@ -0,0 +1,183 @@
/*
* Intel Low Power Subsystem PWM controller driver
*
* Copyright (C) 2014, Intel Corporation
* Author: Mika Westerberg <mika.westerberg@linux.intel.com>
* Author: Chew Kean Ho <kean.ho.chew@intel.com>
* Author: Chang Rebecca Swee Fun <rebecca.swee.fun.chang@intel.com>
* Author: Chew Chiau Ee <chiau.ee.chew@intel.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/acpi.h>
#include <linux/clk.h>
#include <linux/device.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/pwm.h>
#include <linux/platform_device.h>
#define PWM 0x00000000
#define PWM_ENABLE BIT(31)
#define PWM_SW_UPDATE BIT(30)
#define PWM_BASE_UNIT_SHIFT 8
#define PWM_BASE_UNIT_MASK 0x00ffff00
#define PWM_ON_TIME_DIV_MASK 0x000000ff
#define PWM_DIVISION_CORRECTION 0x2
#define PWM_LIMIT (0x8000 + PWM_DIVISION_CORRECTION)
#define NSECS_PER_SEC 1000000000UL
struct pwm_lpss_chip {
struct pwm_chip chip;
void __iomem *regs;
struct clk *clk;
};
static inline struct pwm_lpss_chip *to_lpwm(struct pwm_chip *chip)
{
return container_of(chip, struct pwm_lpss_chip, chip);
}
static int pwm_lpss_config(struct pwm_chip *chip, struct pwm_device *pwm,
int duty_ns, int period_ns)
{
struct pwm_lpss_chip *lpwm = to_lpwm(chip);
u8 on_time_div;
unsigned long c;
unsigned long long base_unit, freq = NSECS_PER_SEC;
u32 ctrl;
do_div(freq, period_ns);
/* The equation is: base_unit = ((freq / c) * 65536) + correction */
base_unit = freq * 65536;
c = clk_get_rate(lpwm->clk);
if (!c)
return -EINVAL;
do_div(base_unit, c);
base_unit += PWM_DIVISION_CORRECTION;
if (base_unit > PWM_LIMIT)
return -EINVAL;
if (duty_ns <= 0)
duty_ns = 1;
on_time_div = 255 - (255 * duty_ns / period_ns);
ctrl = readl(lpwm->regs + PWM);
ctrl &= ~(PWM_BASE_UNIT_MASK | PWM_ON_TIME_DIV_MASK);
ctrl |= (u16) base_unit << PWM_BASE_UNIT_SHIFT;
ctrl |= on_time_div;
/* request PWM to update on next cycle */
ctrl |= PWM_SW_UPDATE;
writel(ctrl, lpwm->regs + PWM);
return 0;
}
static int pwm_lpss_enable(struct pwm_chip *chip, struct pwm_device *pwm)
{
struct pwm_lpss_chip *lpwm = to_lpwm(chip);
u32 ctrl;
int ret;
ret = clk_prepare_enable(lpwm->clk);
if (ret)
return ret;
ctrl = readl(lpwm->regs + PWM);
writel(ctrl | PWM_ENABLE, lpwm->regs + PWM);
return 0;
}
static void pwm_lpss_disable(struct pwm_chip *chip, struct pwm_device *pwm)
{
struct pwm_lpss_chip *lpwm = to_lpwm(chip);
u32 ctrl;
ctrl = readl(lpwm->regs + PWM);
writel(ctrl & ~PWM_ENABLE, lpwm->regs + PWM);
clk_disable_unprepare(lpwm->clk);
}
static const struct pwm_ops pwm_lpss_ops = {
.config = pwm_lpss_config,
.enable = pwm_lpss_enable,
.disable = pwm_lpss_disable,
.owner = THIS_MODULE,
};
static const struct acpi_device_id pwm_lpss_acpi_match[] = {
{ "80860F09", 0 },
{ },
};
MODULE_DEVICE_TABLE(acpi, pwm_lpss_acpi_match);
static int pwm_lpss_probe(struct platform_device *pdev)
{
struct pwm_lpss_chip *lpwm;
struct resource *r;
int ret;
lpwm = devm_kzalloc(&pdev->dev, sizeof(*lpwm), GFP_KERNEL);
if (!lpwm)
return -ENOMEM;
r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
lpwm->regs = devm_ioremap_resource(&pdev->dev, r);
if (IS_ERR(lpwm->regs))
return PTR_ERR(lpwm->regs);
lpwm->clk = devm_clk_get(&pdev->dev, NULL);
if (IS_ERR(lpwm->clk)) {
dev_err(&pdev->dev, "failed to get PWM clock\n");
return PTR_ERR(lpwm->clk);
}
lpwm->chip.dev = &pdev->dev;
lpwm->chip.ops = &pwm_lpss_ops;
lpwm->chip.base = -1;
lpwm->chip.npwm = 1;
ret = pwmchip_add(&lpwm->chip);
if (ret) {
dev_err(&pdev->dev, "failed to add PWM chip: %d\n", ret);
return ret;
}
platform_set_drvdata(pdev, lpwm);
return 0;
}
static int pwm_lpss_remove(struct platform_device *pdev)
{
struct pwm_lpss_chip *lpwm = platform_get_drvdata(pdev);
u32 ctrl;
ctrl = readl(lpwm->regs + PWM);
writel(ctrl & ~PWM_ENABLE, lpwm->regs + PWM);
return pwmchip_remove(&lpwm->chip);
}
static struct platform_driver pwm_lpss_driver = {
.driver = {
.name = "pwm-lpss",
.acpi_match_table = pwm_lpss_acpi_match,
},
.probe = pwm_lpss_probe,
.remove = pwm_lpss_remove,
};
module_platform_driver(pwm_lpss_driver);
MODULE_DESCRIPTION("PWM driver for Intel LPSS");
MODULE_AUTHOR("Mika Westerberg <mika.westerberg@linux.intel.com>");
MODULE_LICENSE("GPL v2");
MODULE_ALIAS("platform:pwm-lpss");

View File

@ -127,12 +127,12 @@ static struct pwm_ops pxa_pwm_ops = {
#ifdef CONFIG_OF #ifdef CONFIG_OF
/* /*
* Device tree users must create one device instance for each pwm channel. * Device tree users must create one device instance for each PWM channel.
* Hence we dispense with the HAS_SECONDARY_PWM and "tell" the original driver * Hence we dispense with the HAS_SECONDARY_PWM and "tell" the original driver
* code that this is a single channel pxa25x-pwm. Currently all devices are * code that this is a single channel pxa25x-pwm. Currently all devices are
* supported identically. * supported identically.
*/ */
static struct of_device_id pwm_of_match[] = { static const struct of_device_id pwm_of_match[] = {
{ .compatible = "marvell,pxa250-pwm", .data = &pwm_id_table[0]}, { .compatible = "marvell,pxa250-pwm", .data = &pwm_id_table[0]},
{ .compatible = "marvell,pxa270-pwm", .data = &pwm_id_table[0]}, { .compatible = "marvell,pxa270-pwm", .data = &pwm_id_table[0]},
{ .compatible = "marvell,pxa168-pwm", .data = &pwm_id_table[0]}, { .compatible = "marvell,pxa168-pwm", .data = &pwm_id_table[0]},

View File

@ -598,9 +598,8 @@ static int pwm_samsung_resume(struct device *dev)
} }
#endif #endif
static const struct dev_pm_ops pwm_samsung_pm_ops = { static SIMPLE_DEV_PM_OPS(pwm_samsung_pm_ops, pwm_samsung_suspend,
SET_SYSTEM_SLEEP_PM_OPS(pwm_samsung_suspend, pwm_samsung_resume) pwm_samsung_resume);
};
static struct platform_driver pwm_samsung_driver = { static struct platform_driver pwm_samsung_driver = {
.driver = { .driver = {

View File

@ -7,7 +7,7 @@
struct pwm_device; struct pwm_device;
struct seq_file; struct seq_file;
#if IS_ENABLED(CONFIG_PWM) || IS_ENABLED(CONFIG_HAVE_PWM) #if IS_ENABLED(CONFIG_PWM)
/* /*
* pwm_request - request a PWM device * pwm_request - request a PWM device
*/ */