Commit 5b9c8972 authored by Linus Torvalds's avatar Linus Torvalds

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

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

Pull pwm changes from Thierry Reding:
 "There are no new drivers here, only a couple of fixes all over the
  place"

* tag 'pwm/for-3.18-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/thierry.reding/linux-pwm:
  pwm: Let PWM_CLPS711X depend on HAS_IOMEM
  pwm: atmel: Fix calculation of prescale value
  pwm: Fix uninitialized warnings in pwm_get()
  pwm: rockchip: Allow polarity invert on rk3288
  pwm: imx: Avoid sample FIFO overflow for i.MX PWM version2
  pwm: imx: Cleanup indentation for register definitions
  pwm: imx: Fix the macro MX3_PWMCR_PRESCALER(x) definition
  pwm: Fix possible ZERO_SIZE_PTR pointer dereferencing error.
  pwm: lpss: make it buildable only on X86
  pwm: lpss: use c99 initializers in structures
  pwm: lpss: Fix build failure on PowerPC
  pwm: lpss: pci: Move to use pcim_enable_device()
  pwm: lpss: Properly split driver to parts
  pwm: lpss: Add ACPI and PCI IDs for Intel Braswell
  pwm: fsl-ftm: Select REGMAP_MMIO
  pwm: fsl-ftm: Document 'big-endian' property
  pwm: fsl-ftm: Convert to direct regmap API usage
  pwm: fsl-ftm: Clean up the code
parents 3d430bdb dec02f98
Freescale FlexTimer Module (FTM) PWM controller
The same FTM PWM device can have a different endianness on different SoCs. The
device tree provides a property to describing this so that an operating system
device driver can handle all variants of the device. Refer to the table below
for the endianness of the FTM PWM block as integrated into the existing SoCs:
SoC | FTM-PWM endianness
--------+-------------------
Vybrid | LE
LS1 | BE
LS2 | LE
Please see ../regmap/regmap.txt for more detail about how to specify endian
modes in device tree.
Required properties:
- compatible: Should be "fsl,vf610-ftm-pwm".
- reg: Physical base address and length of the controller's registers
......@@ -16,7 +31,8 @@ Required properties:
- 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.
- big-endian: Boolean property, required if the FTM PWM registers use a big-
endian rather than little-endian layout.
Example:
......@@ -32,4 +48,5 @@ pwm0: pwm@40038000 {
<&clks VF610_CLK_FTM0_EXT_FIX_EN>;
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_pwm0_1>;
big-endian;
};
......@@ -7,8 +7,8 @@ Required properties:
"rockchip,vop-pwm": found integrated in VOP on RK3288 SoC
- reg: physical base address and length of the controller's registers
- clocks: phandle and clock specifier of the PWM reference clock
- #pwm-cells: should be 2. See pwm.txt in this directory for a
description of the cell format.
- #pwm-cells: must be 2 (rk2928) or 3 (rk3288). See pwm.txt in this directory
for a description of the cell format.
Example:
......
......@@ -83,6 +83,7 @@ config PWM_BFIN
config PWM_CLPS711X
tristate "CLPS711X PWM support"
depends on ARCH_CLPS711X || COMPILE_TEST
depends on HAS_IOMEM
help
Generic PWM framework driver for Cirrus Logic CLPS711X.
......@@ -101,6 +102,7 @@ config PWM_EP93XX
config PWM_FSL_FTM
tristate "Freescale FlexTimer Module (FTM) PWM support"
depends on OF
select REGMAP_MMIO
help
Generic FTM PWM framework driver for Freescale VF610 and
Layerscape LS-1 SoCs.
......@@ -149,7 +151,7 @@ config PWM_LPC32XX
config PWM_LPSS
tristate "Intel LPSS PWM support"
depends on ACPI
depends on X86
help
Generic PWM framework driver for Intel Low Power Subsystem PWM
controller.
......@@ -157,6 +159,24 @@ config PWM_LPSS
To compile this driver as a module, choose M here: the module
will be called pwm-lpss.
config PWM_LPSS_PCI
tristate "Intel LPSS PWM PCI driver"
depends on PWM_LPSS && PCI
help
The PCI 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-pci.
config PWM_LPSS_PLATFORM
tristate "Intel LPSS PWM platform driver"
depends on PWM_LPSS && ACPI
help
The platform 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-platform.
config PWM_MXS
tristate "Freescale MXS PWM support"
depends on ARCH_MXS && OF
......
......@@ -13,6 +13,8 @@ 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_LPSS_PCI) += pwm-lpss-pci.o
obj-$(CONFIG_PWM_LPSS_PLATFORM) += pwm-lpss-platform.o
obj-$(CONFIG_PWM_MXS) += pwm-mxs.o
obj-$(CONFIG_PWM_PCA9685) += pwm-pca9685.o
obj-$(CONFIG_PWM_PUV3) += pwm-puv3.o
......
......@@ -236,7 +236,7 @@ int pwmchip_add(struct pwm_chip *chip)
int ret;
if (!chip || !chip->dev || !chip->ops || !chip->ops->config ||
!chip->ops->enable || !chip->ops->disable)
!chip->ops->enable || !chip->ops->disable || !chip->npwm)
return -EINVAL;
mutex_lock(&pwm_lock);
......@@ -602,12 +602,9 @@ struct pwm_device *pwm_get(struct device *dev, const char *con_id)
struct pwm_device *pwm = ERR_PTR(-EPROBE_DEFER);
const char *dev_id = dev ? dev_name(dev) : NULL;
struct pwm_chip *chip = NULL;
unsigned int index = 0;
unsigned int best = 0;
struct pwm_lookup *p;
struct pwm_lookup *p, *chosen = NULL;
unsigned int match;
unsigned int period;
enum pwm_polarity polarity;
/* look up via DT first */
if (IS_ENABLED(CONFIG_OF) && dev && dev->of_node)
......@@ -653,10 +650,7 @@ struct pwm_device *pwm_get(struct device *dev, const char *con_id)
}
if (match > best) {
chip = pwmchip_find_by_name(p->provider);
index = p->index;
period = p->period;
polarity = p->polarity;
chosen = p;
if (match != 3)
best = match;
......@@ -665,17 +659,22 @@ struct pwm_device *pwm_get(struct device *dev, const char *con_id)
}
}
mutex_unlock(&pwm_lookup_lock);
if (!chosen)
goto out;
if (chip)
pwm = pwm_request_from_chip(chip, index, con_id ?: dev_id);
if (IS_ERR(pwm))
return pwm;
chip = pwmchip_find_by_name(chosen->provider);
if (!chip)
goto out;
pwm_set_period(pwm, period);
pwm_set_polarity(pwm, polarity);
pwm = pwm_request_from_chip(chip, chosen->index, con_id ?: dev_id);
if (IS_ERR(pwm))
goto out;
pwm_set_period(pwm, chosen->period);
pwm_set_polarity(pwm, chosen->polarity);
out:
mutex_unlock(&pwm_lookup_lock);
return pwm;
}
EXPORT_SYMBOL_GPL(pwm_get);
......
......@@ -102,7 +102,7 @@ static int atmel_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
int duty_ns, int period_ns)
{
struct atmel_pwm_chip *atmel_pwm = to_atmel_pwm_chip(chip);
unsigned long clk_rate, prd, dty;
unsigned long prd, dty;
unsigned long long div;
unsigned int pres = 0;
u32 val;
......@@ -113,20 +113,18 @@ static int atmel_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
return -EBUSY;
}
clk_rate = clk_get_rate(atmel_pwm->clk);
div = clk_rate;
/* Calculate the period cycles and prescale value */
div = (unsigned long long)clk_get_rate(atmel_pwm->clk) * period_ns;
do_div(div, NSEC_PER_SEC);
/* Calculate the period cycles */
while (div > PWM_MAX_PRD) {
div = clk_rate / (1 << pres);
div = div * period_ns;
/* 1/Hz = 100000000 ns */
do_div(div, 1000000000);
if (pres++ > PRD_MAX_PRES) {
dev_err(chip->dev, "pres exceeds the maximum value\n");
return -EINVAL;
}
div >>= 1;
pres++;
}
if (pres > PRD_MAX_PRES) {
dev_err(chip->dev, "pres exceeds the maximum value\n");
return -EINVAL;
}
/* Calculate the duty cycles */
......
......@@ -18,14 +18,14 @@
#include <linux/of_address.h>
#include <linux/platform_device.h>
#include <linux/pwm.h>
#include <linux/regmap.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_CLK_MASK_SHIFT 3
#define FTM_SC_CLK_MASK (3 << FTM_SC_CLK_MASK_SHIFT)
#define FTM_SC_CLK(c) (((c) + 1) << FTM_SC_CLK_MASK_SHIFT)
#define FTM_SC_PS_MASK 0x7
#define FTM_SC_PS_SHIFT 0
#define FTM_CNT 0x04
#define FTM_MOD 0x08
......@@ -83,7 +83,7 @@ struct fsl_pwm_chip {
unsigned int cnt_select;
unsigned int clk_ps;
void __iomem *base;
struct regmap *regmap;
int period_ns;
......@@ -219,10 +219,11 @@ 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;
unsigned long long duty;
u32 val;
val = readl(fpc->base + FTM_MOD);
duty = duty_ns * (val + 1);
regmap_read(fpc->regmap, FTM_MOD, &val);
duty = (unsigned long long)duty_ns * (val + 1);
do_div(duty, period_ns);
return (unsigned long)duty;
......@@ -232,7 +233,7 @@ 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;
u32 period, duty;
mutex_lock(&fpc->lock);
......@@ -257,11 +258,9 @@ static int fsl_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
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);
regmap_update_bits(fpc->regmap, FTM_SC, FTM_SC_PS_MASK,
fpc->clk_ps);
regmap_write(fpc->regmap, FTM_MOD, period - 1);
fpc->period_ns = period_ns;
}
......@@ -270,8 +269,9 @@ static int fsl_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
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));
regmap_write(fpc->regmap, FTM_CSC(pwm->hwpwm),
FTM_CSC_MSB | FTM_CSC_ELSB);
regmap_write(fpc->regmap, FTM_CV(pwm->hwpwm), duty);
return 0;
}
......@@ -283,31 +283,28 @@ static int fsl_pwm_set_polarity(struct pwm_chip *chip,
struct fsl_pwm_chip *fpc = to_fsl_chip(chip);
u32 val;
val = readl(fpc->base + FTM_POL);
regmap_read(fpc->regmap, FTM_POL, &val);
if (polarity == PWM_POLARITY_INVERSED)
val |= BIT(pwm->hwpwm);
else
val &= ~BIT(pwm->hwpwm);
writel(val, fpc->base + FTM_POL);
regmap_write(fpc->regmap, FTM_POL, val);
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);
regmap_update_bits(fpc->regmap, FTM_SC, FTM_SC_CLK_MASK,
FTM_SC_CLK(fpc->cnt_select));
ret = clk_prepare_enable(fpc->clk[fpc->cnt_select]);
if (ret)
......@@ -327,13 +324,10 @@ static int fsl_counter_clock_enable(struct fsl_pwm_chip *fpc)
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);
regmap_update_bits(fpc->regmap, FTM_OUTMASK, BIT(pwm->hwpwm), 0);
ret = fsl_counter_clock_enable(fpc);
mutex_unlock(&fpc->lock);
......@@ -343,8 +337,6 @@ static int fsl_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm)
static void fsl_counter_clock_disable(struct fsl_pwm_chip *fpc)
{
u32 val;
/*
* already disabled, do nothing
*/
......@@ -356,9 +348,7 @@ static void fsl_counter_clock_disable(struct fsl_pwm_chip *fpc)
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);
regmap_update_bits(fpc->regmap, FTM_SC, FTM_SC_CLK_MASK, 0);
clk_disable_unprepare(fpc->clk[FSL_PWM_CLK_CNTEN]);
clk_disable_unprepare(fpc->clk[fpc->cnt_select]);
......@@ -370,14 +360,12 @@ static void fsl_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm)
u32 val;
mutex_lock(&fpc->lock);
val = readl(fpc->base + FTM_OUTMASK);
val |= BIT(pwm->hwpwm);
writel(val, fpc->base + FTM_OUTMASK);
regmap_update_bits(fpc->regmap, FTM_OUTMASK, BIT(pwm->hwpwm),
BIT(pwm->hwpwm));
fsl_counter_clock_disable(fpc);
val = readl(fpc->base + FTM_OUTMASK);
regmap_read(fpc->regmap, FTM_OUTMASK, &val);
if ((val & 0xFF) == 0xFF)
fpc->period_ns = 0;
......@@ -402,19 +390,28 @@ static int fsl_pwm_init(struct fsl_pwm_chip *fpc)
if (ret)
return ret;
writel(0x00, fpc->base + FTM_CNTIN);
writel(0x00, fpc->base + FTM_OUTINIT);
writel(0xFF, fpc->base + FTM_OUTMASK);
regmap_write(fpc->regmap, FTM_CNTIN, 0x00);
regmap_write(fpc->regmap, FTM_OUTINIT, 0x00);
regmap_write(fpc->regmap, FTM_OUTMASK, 0xFF);
clk_disable_unprepare(fpc->clk[FSL_PWM_CLK_SYS]);
return 0;
}
static const struct regmap_config fsl_pwm_regmap_config = {
.reg_bits = 32,
.reg_stride = 4,
.val_bits = 32,
.max_register = FTM_PWMLOAD,
};
static int fsl_pwm_probe(struct platform_device *pdev)
{
struct fsl_pwm_chip *fpc;
struct resource *res;
void __iomem *base;
int ret;
fpc = devm_kzalloc(&pdev->dev, sizeof(*fpc), GFP_KERNEL);
......@@ -426,9 +423,16 @@ static int fsl_pwm_probe(struct platform_device *pdev)
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);
base = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(base))
return PTR_ERR(base);
fpc->regmap = devm_regmap_init_mmio_clk(&pdev->dev, NULL, base,
&fsl_pwm_regmap_config);
if (IS_ERR(fpc->regmap)) {
dev_err(&pdev->dev, "regmap init failed\n");
return PTR_ERR(fpc->regmap);
}
fpc->clk[FSL_PWM_CLK_SYS] = devm_clk_get(&pdev->dev, "ftm_sys");
if (IS_ERR(fpc->clk[FSL_PWM_CLK_SYS])) {
......
......@@ -14,6 +14,7 @@
#include <linux/slab.h>
#include <linux/err.h>
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/io.h>
#include <linux/pwm.h>
#include <linux/of.h>
......@@ -21,24 +22,30 @@
/* i.MX1 and i.MX21 share the same PWM function block: */
#define MX1_PWMC 0x00 /* PWM Control Register */
#define MX1_PWMS 0x04 /* PWM Sample Register */
#define MX1_PWMP 0x08 /* PWM Period Register */
#define MX1_PWMC 0x00 /* PWM Control Register */
#define MX1_PWMS 0x04 /* PWM Sample Register */
#define MX1_PWMP 0x08 /* PWM Period Register */
#define MX1_PWMC_EN (1 << 4)
#define MX1_PWMC_EN (1 << 4)
/* i.MX27, i.MX31, i.MX35 share the same PWM function block: */
#define MX3_PWMCR 0x00 /* PWM Control Register */
#define MX3_PWMSAR 0x0C /* PWM Sample Register */
#define MX3_PWMPR 0x10 /* PWM Period Register */
#define MX3_PWMCR_PRESCALER(x) (((x - 1) & 0xFFF) << 4)
#define MX3_PWMCR_DOZEEN (1 << 24)
#define MX3_PWMCR_WAITEN (1 << 23)
#define MX3_PWMCR 0x00 /* PWM Control Register */
#define MX3_PWMSR 0x04 /* PWM Status Register */
#define MX3_PWMSAR 0x0C /* PWM Sample Register */
#define MX3_PWMPR 0x10 /* PWM Period Register */
#define MX3_PWMCR_PRESCALER(x) ((((x) - 1) & 0xFFF) << 4)
#define MX3_PWMCR_DOZEEN (1 << 24)
#define MX3_PWMCR_WAITEN (1 << 23)
#define MX3_PWMCR_DBGEN (1 << 22)
#define MX3_PWMCR_CLKSRC_IPG_HIGH (2 << 16)
#define MX3_PWMCR_CLKSRC_IPG (1 << 16)
#define MX3_PWMCR_EN (1 << 0)
#define MX3_PWMCR_CLKSRC_IPG_HIGH (2 << 16)
#define MX3_PWMCR_CLKSRC_IPG (1 << 16)
#define MX3_PWMCR_SWR (1 << 3)
#define MX3_PWMCR_EN (1 << 0)
#define MX3_PWMSR_FIFOAV_4WORDS 0x4
#define MX3_PWMSR_FIFOAV_MASK 0x7
#define MX3_PWM_SWR_LOOP 5
struct imx_chip {
struct clk *clk_per;
......@@ -103,9 +110,43 @@ static int imx_pwm_config_v2(struct pwm_chip *chip,
struct pwm_device *pwm, int duty_ns, int period_ns)
{
struct imx_chip *imx = to_imx_chip(chip);
struct device *dev = chip->dev;
unsigned long long c;
unsigned long period_cycles, duty_cycles, prescale;
u32 cr;
unsigned int period_ms;
bool enable = test_bit(PWMF_ENABLED, &pwm->flags);
int wait_count = 0, fifoav;
u32 cr, sr;
/*
* i.MX PWMv2 has a 4-word sample FIFO.
* In order to avoid FIFO overflow issue, we do software reset
* to clear all sample FIFO if the controller is disabled or
* wait for a full PWM cycle to get a relinquished FIFO slot
* when the controller is enabled and the FIFO is fully loaded.
*/
if (enable) {
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);
msleep(period_ms);
sr = readl(imx->mmio_base + MX3_PWMSR);
if (fifoav == (sr & MX3_PWMSR_FIFOAV_MASK))
dev_warn(dev, "there is no free FIFO slot\n");
}
} else {
writel(MX3_PWMCR_SWR, imx->mmio_base + MX3_PWMCR);
do {
usleep_range(200, 1000);
cr = readl(imx->mmio_base + MX3_PWMCR);
} while ((cr & MX3_PWMCR_SWR) &&
(wait_count++ < MX3_PWM_SWR_LOOP));
if (cr & MX3_PWMCR_SWR)
dev_warn(dev, "software reset timeout\n");
}
c = clk_get_rate(imx->clk_per);
c = c * period_ns;
......@@ -135,7 +176,7 @@ static int imx_pwm_config_v2(struct pwm_chip *chip,
MX3_PWMCR_DOZEEN | MX3_PWMCR_WAITEN |
MX3_PWMCR_DBGEN | MX3_PWMCR_CLKSRC_IPG_HIGH;
if (test_bit(PWMF_ENABLED, &pwm->flags))
if (enable)
cr |= MX3_PWMCR_EN;
writel(cr, imx->mmio_base + MX3_PWMCR);
......
/*
* Intel Low Power Subsystem PWM controller PCI driver
*
* Copyright (C) 2014, Intel Corporation
*
* Derived from the original pwm-lpss.c
*
* 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/kernel.h>
#include <linux/module.h>
#include <linux/pci.h>
#include "pwm-lpss.h"
static int pwm_lpss_probe_pci(struct pci_dev *pdev,
const struct pci_device_id *id)
{
const struct pwm_lpss_boardinfo *info;
struct pwm_lpss_chip *lpwm;
int err;
err = pcim_enable_device(pdev);
if (err < 0)
return err;
info = (struct pwm_lpss_boardinfo *)id->driver_data;
lpwm = pwm_lpss_probe(&pdev->dev, &pdev->resource[0], info);
if (IS_ERR(lpwm))
return PTR_ERR(lpwm);
pci_set_drvdata(pdev, lpwm);
return 0;
}
static void pwm_lpss_remove_pci(struct pci_dev *pdev)
{
struct pwm_lpss_chip *lpwm = pci_get_drvdata(pdev);
pwm_lpss_remove(lpwm);
}
static const struct pci_device_id pwm_lpss_pci_ids[] = {
{ PCI_VDEVICE(INTEL, 0x0f08), (unsigned long)&pwm_lpss_byt_info},
{ PCI_VDEVICE(INTEL, 0x0f09), (unsigned long)&pwm_lpss_byt_info},
{ PCI_VDEVICE(INTEL, 0x2288), (unsigned long)&pwm_lpss_bsw_info},
{ PCI_VDEVICE(INTEL, 0x2289), (unsigned long)&pwm_lpss_bsw_info},
{ },
};
MODULE_DEVICE_TABLE(pci, pwm_lpss_pci_ids);
static struct pci_driver pwm_lpss_driver_pci = {
.name = "pwm-lpss",
.id_table = pwm_lpss_pci_ids,
.probe = pwm_lpss_probe_pci,
.remove = pwm_lpss_remove_pci,
};
module_pci_driver(pwm_lpss_driver_pci);
MODULE_DESCRIPTION("PWM PCI driver for Intel LPSS");
MODULE_LICENSE("GPL v2");
/*
* Intel Low Power Subsystem PWM controller driver
*
* Copyright (C) 2014, Intel Corporation
*
* Derived from the original pwm-lpss.c
*
* 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/kernel.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include "pwm-lpss.h"
static int pwm_lpss_probe_platform(struct platform_device *pdev)
{
const struct pwm_lpss_boardinfo *info;
const struct acpi_device_id *id;
struct pwm_lpss_chip *lpwm;
struct resource *r;
id = acpi_match_device(pdev->dev.driver->acpi_match_table, &pdev->dev);
if (!id)
return -ENODEV;
info = (const struct pwm_lpss_boardinfo *)id->driver_data;
r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
lpwm = pwm_lpss_probe(&pdev->dev, r, info);
if (IS_ERR(lpwm))
return PTR_ERR(lpwm);
platform_set_drvdata(pdev, lpwm);
return 0;
}
static int pwm_lpss_remove_platform(struct platform_device *pdev)
{
struct pwm_lpss_chip *lpwm = platform_get_drvdata(pdev);
return pwm_lpss_remove(lpwm);
}
static const struct acpi_device_id pwm_lpss_acpi_match[] = {
{ "80860F09", (unsigned long)&pwm_lpss_byt_info },
{ "80862288", (unsigned long)&pwm_lpss_bsw_info },
{ },
};
MODULE_DEVICE_TABLE(acpi, pwm_lpss_acpi_match);
static struct platform_driver pwm_lpss_driver_platform = {
.driver = {
.name = "pwm-lpss",
.acpi_match_table = pwm_lpss_acpi_match,
},
.probe = pwm_lpss_probe_platform,
.remove = pwm_lpss_remove_platform,
};
module_platform_driver(pwm_lpss_driver_platform);
MODULE_DESCRIPTION("PWM platform driver for Intel LPSS");
MODULE_LICENSE("GPL v2");
MODULE_ALIAS("platform:pwm-lpss");
......@@ -13,15 +13,11 @@
* published by the Free Software Foundation.
*/
#include <linux/acpi.h>
#include <linux/device.h>
#include <linux/io.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/pwm.h>
#include <linux/platform_device.h>
#include <linux/pci.h>
static int pci_drv, plat_drv; /* So we know which drivers registered */
#include "pwm-lpss.h"
#define PWM 0x00000000
#define PWM_ENABLE BIT(31)
......@@ -39,14 +35,17 @@ struct pwm_lpss_chip {
unsigned long clk_rate;
};
struct pwm_lpss_boardinfo {
unsigned long clk_rate;
/* BayTrail */
const struct pwm_lpss_boardinfo pwm_lpss_byt_info = {
.clk_rate = 25000000
};
EXPORT_SYMBOL_GPL(pwm_lpss_byt_info);
/* BayTrail */
static const struct pwm_lpss_boardinfo byt_info = {
25000000
/* Braswell */
const struct pwm_lpss_boardinfo pwm_lpss_bsw_info = {
.clk_rate = 19200000
};
EXPORT_SYMBOL_GPL(pwm_lpss_bsw_info);
static inline struct pwm_lpss_chip *to_lpwm(struct pwm_chip *chip)
{
......@@ -118,9 +117,8 @@ static const struct pwm_ops pwm_lpss_ops = {
.owner = THIS_MODULE,
};
static struct pwm_lpss_chip *pwm_lpss_probe(struct device *dev,
struct resource *r,
const struct pwm_lpss_boardinfo *info)
struct pwm_lpss_chip *pwm_lpss_probe(struct device *dev, struct resource *r,
const struct pwm_lpss_boardinfo *info)
{
struct pwm_lpss_chip *lpwm;
int ret;
......@@ -147,8 +145,9 @@ static struct pwm_lpss_chip *pwm_lpss_probe(struct device *dev,
return lpwm;
}
EXPORT_SYMBOL_GPL(pwm_lpss_probe);
static int pwm_lpss_remove(struct pwm_lpss_chip *lpwm)
int pwm_lpss_remove(struct pwm_lpss_chip *lpwm)
{
u32 ctrl;
......@@ -157,114 +156,8 @@ static int pwm_lpss_remove(struct pwm_lpss_chip *lpwm)
return pwmchip_remove(&lpwm->chip);
}
static int pwm_lpss_probe_pci(struct pci_dev *pdev,
const struct pci_device_id *id)
{
const struct pwm_lpss_boardinfo *info;
struct pwm_lpss_chip *lpwm;
int err;
err = pci_enable_device(pdev);
if (err < 0)
return err;
info = (struct pwm_lpss_boardinfo *)id->driver_data;
lpwm = pwm_lpss_probe(&pdev->dev, &pdev->resource[0], info);
if (IS_ERR(lpwm))
return PTR_ERR(lpwm);
pci_set_drvdata(pdev, lpwm);
return 0;
}
static void pwm_lpss_remove_pci(struct pci_dev *pdev)
{
struct pwm_lpss_chip *lpwm = pci_get_drvdata(pdev);
pwm_lpss_remove(lpwm);
pci_disable_device(pdev);
}
static struct pci_device_id pwm_lpss_pci_ids[] = {
{ PCI_VDEVICE(INTEL, 0x0f08), (unsigned long)&byt_info},
{ PCI_VDEVICE(INTEL, 0x0f09), (unsigned long)&byt_info},
{ },
};
MODULE_DEVICE_TABLE(pci, pwm_lpss_pci_ids);
static struct pci_driver pwm_lpss_driver_pci = {
.name = "pwm-lpss",
.id_table = pwm_lpss_pci_ids,
.probe = pwm_lpss_probe_pci,
.remove = pwm_lpss_remove_pci,
};
static int pwm_lpss_probe_platform(struct platform_device *pdev)
{
const struct pwm_lpss_boardinfo *info;
const struct acpi_device_id *id;
struct pwm_lpss_chip *lpwm;
struct resource *r;
id = acpi_match_device(pdev->dev.driver->acpi_match_table, &pdev->dev);
if (!id)
return -ENODEV;
r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
info = (struct pwm_lpss_boardinfo *)id->driver_data;
lpwm = pwm_lpss_probe(&pdev->dev, r, info);
if (IS_ERR(lpwm))
return PTR_ERR(lpwm);
platform_set_drvdata(pdev, lpwm);
return 0;
}
static int pwm_lpss_remove_platform(struct platform_device *pdev)
{
struct pwm_lpss_chip *lpwm = platform_get_drvdata(pdev);
return pwm_lpss_remove(lpwm);
}
static const struct acpi_device_id pwm_lpss_acpi_match[] = {
{ "80860F09", (unsigned long)&byt_info },
{ },
};
MODULE_DEVICE_TABLE(acpi, pwm_lpss_acpi_match);
static struct platform_driver pwm_lpss_driver_platform = {
.driver = {
.name = "pwm-lpss",
.acpi_match_table = pwm_lpss_acpi_match,
},
.probe = pwm_lpss_probe_platform,
.remove = pwm_lpss_remove_platform,
};
static int __init pwm_init(void)
{
pci_drv = pci_register_driver(&pwm_lpss_driver_pci);
plat_drv = platform_driver_register(&pwm_lpss_driver_platform);
if (pci_drv && plat_drv)
return pci_drv;
return 0;
}
module_init(pwm_init);
static void __exit pwm_exit(void)
{
if (!pci_drv)
pci_unregister_driver(&pwm_lpss_driver_pci);
if (!plat_drv)
platform_driver_unregister(&pwm_lpss_driver_platform);
}
module_exit(pwm_exit);
EXPORT_SYMBOL_GPL(pwm_lpss_remove);
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");
/*
* Intel Low Power Subsystem PWM controller driver
*
* Copyright (C) 2014, Intel Corporation
*
* Derived from the original pwm-lpss.c
*
* 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.
*/
#ifndef __PWM_LPSS_H
#define __PWM_LPSS_H
#include <linux/device.h>
#include <linux/pwm.h>
struct pwm_lpss_chip;
struct pwm_lpss_boardinfo {
unsigned long clk_rate;
};
extern const struct pwm_lpss_boardinfo pwm_lpss_byt_info;
extern const struct pwm_lpss_boardinfo pwm_lpss_bsw_info;
struct pwm_lpss_chip *pwm_lpss_probe(struct device *dev, struct resource *r,
const struct pwm_lpss_boardinfo *info);
int pwm_lpss_remove(struct pwm_lpss_chip *lpwm);
#endif /* __PWM_LPSS_H */
......@@ -24,7 +24,9 @@
#define PWM_ENABLE (1 << 0)
#define PWM_CONTINUOUS (1 << 1)
#define PWM_DUTY_POSITIVE (1 << 3)
#define PWM_DUTY_NEGATIVE (0 << 3)
#define PWM_INACTIVE_NEGATIVE (0 << 4)
#define PWM_INACTIVE_POSITIVE (1 << 4)
#define PWM_OUTPUT_LEFT (0 << 5)
#define PWM_LP_DISABLE (0 << 8)
......@@ -45,8 +47,10 @@ struct rockchip_pwm_regs {
struct rockchip_pwm_data {
struct rockchip_pwm_regs regs;
unsigned int prescaler;
const struct pwm_ops *ops;
void (*set_enable)(struct pwm_chip *chip, bool enable);
void (*set_enable)(struct pwm_chip *chip,
struct pwm_device *pwm, bool enable);
};
static inline struct rockchip_pwm_chip *to_rockchip_pwm_chip(struct pwm_chip *c)
......@@ -54,7 +58,8 @@ static inline struct rockchip_pwm_chip *to_rockchip_pwm_chip(struct pwm_chip *c)
return container_of(c, struct rockchip_pwm_chip, chip);
}
static void rockchip_pwm_set_enable_v1(struct pwm_chip *chip, bool enable)
static void rockchip_pwm_set_enable_v1(struct pwm_chip *chip,
struct pwm_device *pwm, bool enable)
{
struct rockchip_pwm_chip *pc = to_rockchip_pwm_chip(chip);
u32 enable_conf = PWM_CTRL_OUTPUT_EN | PWM_CTRL_TIMER_EN;
......@@ -70,14 +75,19 @@ static void rockchip_pwm_set_enable_v1(struct pwm_chip *chip, bool enable)
writel_relaxed(val, pc->base + pc->data->regs.ctrl);
}
static void rockchip_pwm_set_enable_v2(struct pwm_chip *chip, bool enable)
static void rockchip_pwm_set_enable_v2(struct pwm_chip *chip,
struct pwm_device *pwm, bool enable)
{
struct rockchip_pwm_chip *pc = to_rockchip_pwm_chip(chip);
u32 enable_conf = PWM_OUTPUT_LEFT | PWM_LP_DISABLE | PWM_ENABLE |
PWM_CONTINUOUS | PWM_DUTY_POSITIVE |
PWM_INACTIVE_NEGATIVE;
PWM_CONTINUOUS;
u32 val;
if (pwm->polarity == PWM_POLARITY_INVERSED)
enable_conf |= PWM_DUTY_NEGATIVE | PWM_INACTIVE_POSITIVE;
else
enable_conf |= PWM_DUTY_POSITIVE | PWM_INACTIVE_NEGATIVE;
val = readl_relaxed(pc->base + pc->data->regs.ctrl);
if (enable)
......@@ -124,6 +134,19 @@ static int rockchip_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
return 0;
}
static int rockchip_pwm_set_polarity(struct pwm_chip *chip,
struct pwm_device *pwm,
enum pwm_polarity polarity)
{
/*
* No action needed here because pwm->polarity will be set by the core
* and the core will only change polarity when the PWM is not enabled.
* We'll handle things in set_enable().
*/
return 0;
}
static int rockchip_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm)
{
struct rockchip_pwm_chip *pc = to_rockchip_pwm_chip(chip);
......@@ -133,7 +156,7 @@ static int rockchip_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm)
if (ret)
return ret;
pc->data->set_enable(chip, true);
pc->data->set_enable(chip, pwm, true);
return 0;
}
......@@ -142,18 +165,26 @@ static void rockchip_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm)
{
struct rockchip_pwm_chip *pc = to_rockchip_pwm_chip(chip);
pc->data->set_enable(chip, false);
pc->data->set_enable(chip, pwm, false);
clk_disable(pc->clk);
}
static const struct pwm_ops rockchip_pwm_ops = {
static const struct pwm_ops rockchip_pwm_ops_v1 = {
.config = rockchip_pwm_config,
.enable = rockchip_pwm_enable,
.disable = rockchip_pwm_disable,
.owner = THIS_MODULE,
};
static const struct pwm_ops rockchip_pwm_ops_v2 = {
.config = rockchip_pwm_config,
.set_polarity = rockchip_pwm_set_polarity,
.enable = rockchip_pwm_enable,
.disable = rockchip_pwm_disable,
.owner = THIS_MODULE,
};
static const struct rockchip_pwm_data pwm_data_v1 = {
.regs = {
.duty = 0x04,
......@@ -162,6 +193,7 @@ static const struct rockchip_pwm_data pwm_data_v1 = {
.ctrl = 0x0c,
},
.prescaler = 2,
.ops = &rockchip_pwm_ops_v1,
.set_enable = rockchip_pwm_set_enable_v1,
};
......@@ -173,6 +205,7 @@ static const struct rockchip_pwm_data pwm_data_v2 = {
.ctrl = 0x0c,
},
.prescaler = 1,
.ops = &rockchip_pwm_ops_v2,
.set_enable = rockchip_pwm_set_enable_v2,
};
......@@ -184,6 +217,7 @@ static const struct rockchip_pwm_data pwm_data_vop = {
.ctrl = 0x00,
},
.prescaler = 1,
.ops = &rockchip_pwm_ops_v2,
.set_enable = rockchip_pwm_set_enable_v2,
};
......@@ -227,10 +261,15 @@ static int rockchip_pwm_probe(struct platform_device *pdev)
pc->data = id->data;
pc->chip.dev = &pdev->dev;
pc->chip.ops = &rockchip_pwm_ops;
pc->chip.ops = pc->data->ops;
pc->chip.base = -1;
pc->chip.npwm = 1;
if (pc->data->ops->set_polarity) {
pc->chip.of_xlate = of_pwm_xlate_with_flags;
pc->chip.of_pwm_n_cells = 3;
}
ret = pwmchip_add(&pc->chip);
if (ret < 0) {
clk_unprepare(pc->clk);
......
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