Commit 9712d3c3 authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'pwm/for-3.15-rc1' of...

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
parents 2bf73dd6 2ae69a46
* 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>;
};
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>;
};
......@@ -113,9 +113,6 @@ config ARM_DMA_IOMMU_ALIGNMENT
endif
config HAVE_PWM
bool
config MIGHT_HAVE_PCI
bool
......@@ -633,7 +630,6 @@ config ARCH_LPC32XX
select CPU_ARM926T
select GENERIC_CLOCKEVENTS
select HAVE_IDE
select HAVE_PWM
select USE_OF
help
Support for the NXP LPC32XX family of processors
......
......@@ -7,7 +7,6 @@ comment "Intel/Marvell Dev Platforms (sorted by hardware release time)"
config MACH_PXA3XX_DT
bool "Support PXA3xx platforms from device tree"
select CPU_PXA300
select HAVE_PWM
select POWER_SUPPLY
select PXA3xx
select USE_OF
......@@ -23,12 +22,10 @@ config ARCH_LUBBOCK
config MACH_MAINSTONE
bool "Intel HCDDBBVA0 Development Platform (aka Mainstone)"
select HAVE_PWM
select PXA27x
config MACH_ZYLONITE
bool
select HAVE_PWM
select PXA3xx
config MACH_ZYLONITE300
......@@ -123,7 +120,6 @@ config MACH_CM_X300
bool "CompuLab CM-X300 modules"
select CPU_PXA300
select CPU_PXA310
select HAVE_PWM
select PXA3xx
config MACH_CAPC7117
......@@ -214,7 +210,6 @@ config TRIZEPS_PCMCIA
config MACH_LOGICPD_PXA270
bool "LogicPD PXA270 Card Engine Development Platform"
select HAVE_PWM
select PXA27x
config MACH_PCM027
......@@ -225,7 +220,6 @@ config MACH_PCM027
config MACH_PCM990_BASEBOARD
bool "PHYTEC PCM-990 development board"
depends on MACH_PCM027
select HAVE_PWM
choice
prompt "display on pcm990"
......@@ -249,7 +243,6 @@ config MACH_COLIBRI
config MACH_COLIBRI_PXA270_INCOME
bool "Income s.r.o. PXA270 SBC"
depends on MACH_COLIBRI
select HAVE_PWM
select PXA27x
config MACH_COLIBRI300
......@@ -278,7 +271,6 @@ comment "End-user Products (sorted by vendor name)"
config MACH_H4700
bool "HP iPAQ hx4700"
select HAVE_PWM
select IWMMXT
select PXA27x
......@@ -292,14 +284,12 @@ config MACH_HIMALAYA
config MACH_MAGICIAN
bool "Enable HTC Magician Support"
select HAVE_PWM
select IWMMXT
select PXA27x
config MACH_MIOA701
bool "Mitac Mio A701 Support"
select GPIO_SYSFS
select HAVE_PWM
select IWMMXT
select PXA27x
help
......@@ -309,7 +299,6 @@ config MACH_MIOA701
config PXA_EZX
bool "Motorola EZX Platform"
select HAVE_PWM
select IWMMXT
select PXA27x
......@@ -349,7 +338,6 @@ config MACH_MP900C
config ARCH_PXA_PALM
bool "PXA based Palm PDAs"
select HAVE_PWM
config MACH_PALM27X
bool
......@@ -447,7 +435,6 @@ config MACH_TREO680
config MACH_RAUMFELD_RC
bool "Raumfeld Controller"
select CPU_PXA300
select HAVE_PWM
select POWER_SUPPLY
select PXA3xx
......@@ -611,7 +598,6 @@ config MACH_E800
config MACH_ZIPIT2
bool "Zipit Z2 Handheld"
select HAVE_PWM
select PXA27x
endmenu
......
......@@ -156,7 +156,7 @@ config INPUT_MAX8925_ONKEY
config INPUT_MAX8997_HAPTIC
tristate "MAXIM MAX8997 haptic controller support"
depends on PWM && HAVE_PWM && MFD_MAX8997
depends on PWM && MFD_MAX8997
select INPUT_FF_MEMLESS
help
This option enables device driver support for the haptic controller
......@@ -470,7 +470,7 @@ config INPUT_PCF8574
config INPUT_PWM_BEEPER
tristate "PWM beeper support"
depends on PWM && HAVE_PWM
depends on PWM
help
Say Y here to get support for PWM based beeper devices.
......
......@@ -71,6 +71,15 @@ config PWM_BFIN
To compile this driver as a module, choose M here: the module
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
tristate "Cirrus Logic EP93xx PWM support"
depends on ARCH_EP93XX
......@@ -80,6 +89,16 @@ config PWM_EP93XX
To compile this driver as a module, choose M here: the module
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
tristate "i.MX PWM support"
depends on ARCH_MXC
......@@ -119,6 +138,16 @@ config PWM_LPC32XX
To compile this driver as a module, choose M here: the module
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
tristate "Freescale MXS PWM support"
depends on ARCH_MXS && OF
......@@ -160,6 +189,7 @@ config PWM_PXA
config PWM_RENESAS_TPU
tristate "Renesas TPU PWM support"
depends on ARCH_SHMOBILE || COMPILE_TEST
depends on HAS_IOMEM
help
This driver exposes the Timer Pulse Unit (TPU) PWM controller found
in Renesas chips through the PWM API.
......
......@@ -4,11 +4,14 @@ obj-$(CONFIG_PWM_AB8500) += pwm-ab8500.o
obj-$(CONFIG_PWM_ATMEL) += pwm-atmel.o
obj-$(CONFIG_PWM_ATMEL_TCB) += pwm-atmel-tcb.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_FSL_FTM) += pwm-fsl-ftm.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_LPC32XX) += pwm-lpc32xx.o
obj-$(CONFIG_PWM_LPSS) += pwm-lpss.o
obj-$(CONFIG_PWM_MXS) += pwm-mxs.o
obj-$(CONFIG_PWM_PCA9685) += pwm-pca9685.o
obj-$(CONFIG_PWM_PUV3) += pwm-puv3.o
......
......@@ -32,6 +32,7 @@
/* Bit field in CMR */
#define PWM_CMR_CPOL (1 << 9)
#define PWM_CMR_UPD_CDTY (1 << 10)
#define PWM_CMR_CPRE_MSK 0xF
/* The following registers for PWM v1 */
#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 long div;
unsigned int pres = 0;
u32 val;
int ret;
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;
div *= duty_ns;
do_div(div, period_ns);
dty = div;
dty = prd - div;
ret = clk_enable(atmel_pwm->clk);
if (ret) {
......@@ -139,7 +141,10 @@ static int atmel_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
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);
clk_disable(atmel_pwm->clk);
......
/*
* 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");
/*
* 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");
/*
* 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");
......@@ -127,12 +127,12 @@ static struct pwm_ops pxa_pwm_ops = {
#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
* code that this is a single channel pxa25x-pwm. Currently all devices are
* 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,pxa270-pwm", .data = &pwm_id_table[0]},
{ .compatible = "marvell,pxa168-pwm", .data = &pwm_id_table[0]},
......
......@@ -598,9 +598,8 @@ static int pwm_samsung_resume(struct device *dev)
}
#endif
static const struct dev_pm_ops pwm_samsung_pm_ops = {
SET_SYSTEM_SLEEP_PM_OPS(pwm_samsung_suspend, pwm_samsung_resume)
};
static SIMPLE_DEV_PM_OPS(pwm_samsung_pm_ops, pwm_samsung_suspend,
pwm_samsung_resume);
static struct platform_driver pwm_samsung_driver = {
.driver = {
......
......@@ -7,7 +7,7 @@
struct pwm_device;
struct seq_file;
#if IS_ENABLED(CONFIG_PWM) || IS_ENABLED(CONFIG_HAVE_PWM)
#if IS_ENABLED(CONFIG_PWM)
/*
* pwm_request - request a PWM device
*/
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment