Commit 38b0a526 authored by Thierry Reding's avatar Thierry Reding

Merge branch 'for-4.11/drivers' into for-next

parents 776906ff 326ed314
...@@ -6,8 +6,8 @@ Required properties: ...@@ -6,8 +6,8 @@ Required properties:
- "fsl,imx1-pwm" for PWM compatible with the one integrated on i.MX1 - "fsl,imx1-pwm" for PWM compatible with the one integrated on i.MX1
- "fsl,imx27-pwm" for PWM compatible with the one integrated on i.MX27 - "fsl,imx27-pwm" for PWM compatible with the one integrated on i.MX27
- reg: physical base address and length of the controller's registers - reg: physical base address and length of the controller's registers
- #pwm-cells: should be 2. See pwm.txt in this directory for a description of - #pwm-cells: 2 for i.MX1 and 3 for i.MX27 and newer SoCs. See pwm.txt
the cells format. in this directory for a description of the cells format.
- clocks : Clock specifiers for both ipg and per clocks. - clocks : Clock specifiers for both ipg and per clocks.
- clock-names : Clock names should include both "ipg" and "per" - clock-names : Clock names should include both "ipg" and "per"
See the clock consumer binding, See the clock consumer binding,
...@@ -17,7 +17,7 @@ See the clock consumer binding, ...@@ -17,7 +17,7 @@ See the clock consumer binding,
Example: Example:
pwm1: pwm@53fb4000 { pwm1: pwm@53fb4000 {
#pwm-cells = <2>; #pwm-cells = <3>;
compatible = "fsl,imx53-pwm", "fsl,imx27-pwm"; compatible = "fsl,imx53-pwm", "fsl,imx27-pwm";
reg = <0x53fb4000 0x4000>; reg = <0x53fb4000 0x4000>;
clocks = <&clks IMX5_CLK_PWM1_IPG_GATE>, clocks = <&clks IMX5_CLK_PWM1_IPG_GATE>,
......
...@@ -76,7 +76,9 @@ config PWM_ATMEL_TCB ...@@ -76,7 +76,9 @@ config PWM_ATMEL_TCB
config PWM_BCM_IPROC config PWM_BCM_IPROC
tristate "iProc PWM support" tristate "iProc PWM support"
depends on ARCH_BCM_IPROC depends on ARCH_BCM_IPROC || COMPILE_TEST
depends on COMMON_CLK
default ARCH_BCM_IPROC
help help
Generic PWM framework driver for Broadcom iProc PWM block. This Generic PWM framework driver for Broadcom iProc PWM block. This
block is used in Broadcom iProc SoC's. block is used in Broadcom iProc SoC's.
......
...@@ -103,7 +103,7 @@ static void bfin_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm) ...@@ -103,7 +103,7 @@ static void bfin_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm)
disable_gptimer(priv->pin); disable_gptimer(priv->pin);
} }
static struct pwm_ops bfin_pwm_ops = { static const struct pwm_ops bfin_pwm_ops = {
.request = bfin_pwm_request, .request = bfin_pwm_request,
.free = bfin_pwm_free, .free = bfin_pwm_free,
.config = bfin_pwm_config, .config = bfin_pwm_config,
......
...@@ -38,6 +38,7 @@ ...@@ -38,6 +38,7 @@
#define MX3_PWMCR_DOZEEN (1 << 24) #define MX3_PWMCR_DOZEEN (1 << 24)
#define MX3_PWMCR_WAITEN (1 << 23) #define MX3_PWMCR_WAITEN (1 << 23)
#define MX3_PWMCR_DBGEN (1 << 22) #define MX3_PWMCR_DBGEN (1 << 22)
#define MX3_PWMCR_POUTC (1 << 18)
#define MX3_PWMCR_CLKSRC_IPG_HIGH (2 << 16) #define MX3_PWMCR_CLKSRC_IPG_HIGH (2 << 16)
#define MX3_PWMCR_CLKSRC_IPG (1 << 16) #define MX3_PWMCR_CLKSRC_IPG (1 << 16)
#define MX3_PWMCR_SWR (1 << 3) #define MX3_PWMCR_SWR (1 << 3)
...@@ -49,15 +50,10 @@ ...@@ -49,15 +50,10 @@
struct imx_chip { struct imx_chip {
struct clk *clk_per; struct clk *clk_per;
struct clk *clk_ipg;
void __iomem *mmio_base; void __iomem *mmio_base;
struct pwm_chip chip; struct pwm_chip chip;
int (*config)(struct pwm_chip *chip,
struct pwm_device *pwm, int duty_ns, int period_ns);
void (*set_enable)(struct pwm_chip *chip, bool enable);
}; };
#define to_imx_chip(chip) container_of(chip, struct imx_chip, chip) #define to_imx_chip(chip) container_of(chip, struct imx_chip, chip)
...@@ -91,176 +87,170 @@ static int imx_pwm_config_v1(struct pwm_chip *chip, ...@@ -91,176 +87,170 @@ static int imx_pwm_config_v1(struct pwm_chip *chip,
return 0; return 0;
} }
static void imx_pwm_set_enable_v1(struct pwm_chip *chip, bool enable) static int imx_pwm_enable_v1(struct pwm_chip *chip, struct pwm_device *pwm)
{ {
struct imx_chip *imx = to_imx_chip(chip); struct imx_chip *imx = to_imx_chip(chip);
u32 val; u32 val;
int ret;
val = readl(imx->mmio_base + MX1_PWMC); ret = clk_prepare_enable(imx->clk_per);
if (ret < 0)
if (enable) return ret;
val |= MX1_PWMC_EN;
else
val &= ~MX1_PWMC_EN;
val = readl(imx->mmio_base + MX1_PWMC);
val |= MX1_PWMC_EN;
writel(val, imx->mmio_base + MX1_PWMC); writel(val, imx->mmio_base + MX1_PWMC);
}
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;
unsigned int period_ms;
bool enable = pwm_is_enabled(pwm);
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_get_period(pwm),
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;
do_div(c, 1000000000);
period_cycles = c;
prescale = period_cycles / 0x10000 + 1;
period_cycles /= prescale;
c = (unsigned long long)period_cycles * duty_ns;
do_div(c, period_ns);
duty_cycles = c;
/*
* according to imx pwm RM, the real period value should be
* PERIOD value in PWMPR plus 2.
*/
if (period_cycles > 2)
period_cycles -= 2;
else
period_cycles = 0;
writel(duty_cycles, imx->mmio_base + MX3_PWMSAR);
writel(period_cycles, imx->mmio_base + MX3_PWMPR);
cr = MX3_PWMCR_PRESCALER(prescale) |
MX3_PWMCR_DOZEEN | MX3_PWMCR_WAITEN |
MX3_PWMCR_DBGEN | MX3_PWMCR_CLKSRC_IPG_HIGH;
if (enable)
cr |= MX3_PWMCR_EN;
writel(cr, imx->mmio_base + MX3_PWMCR);
return 0; return 0;
} }
static void imx_pwm_set_enable_v2(struct pwm_chip *chip, bool enable) static void imx_pwm_disable_v1(struct pwm_chip *chip, struct pwm_device *pwm)
{ {
struct imx_chip *imx = to_imx_chip(chip); struct imx_chip *imx = to_imx_chip(chip);
u32 val; u32 val;
val = readl(imx->mmio_base + MX3_PWMCR); val = readl(imx->mmio_base + MX1_PWMC);
val &= ~MX1_PWMC_EN;
if (enable) writel(val, imx->mmio_base + MX1_PWMC);
val |= MX3_PWMCR_EN;
else
val &= ~MX3_PWMCR_EN;
writel(val, imx->mmio_base + MX3_PWMCR); clk_disable_unprepare(imx->clk_per);
} }
static int imx_pwm_config(struct pwm_chip *chip, static void imx_pwm_sw_reset(struct pwm_chip *chip)
struct pwm_device *pwm, int duty_ns, int period_ns)
{ {
struct imx_chip *imx = to_imx_chip(chip); struct imx_chip *imx = to_imx_chip(chip);
int ret; struct device *dev = chip->dev;
int wait_count = 0;
ret = clk_prepare_enable(imx->clk_ipg); u32 cr;
if (ret)
return ret; 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");
}
ret = imx->config(chip, pwm, duty_ns, period_ns); static void imx_pwm_wait_fifo_slot(struct pwm_chip *chip,
struct pwm_device *pwm)
{
struct imx_chip *imx = to_imx_chip(chip);
struct device *dev = chip->dev;
unsigned int period_ms;
int fifoav;
u32 sr;
clk_disable_unprepare(imx->clk_ipg); 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_get_period(pwm),
NSEC_PER_MSEC);
msleep(period_ms);
return ret; sr = readl(imx->mmio_base + MX3_PWMSR);
if (fifoav == (sr & MX3_PWMSR_FIFOAV_MASK))
dev_warn(dev, "there is no free FIFO slot\n");
}
} }
static int imx_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm) static int imx_pwm_apply_v2(struct pwm_chip *chip, struct pwm_device *pwm,
struct pwm_state *state)
{ {
unsigned long period_cycles, duty_cycles, prescale;
struct imx_chip *imx = to_imx_chip(chip); struct imx_chip *imx = to_imx_chip(chip);
struct pwm_state cstate;
unsigned long long c;
int ret; int ret;
u32 cr;
pwm_get_state(pwm, &cstate);
if (state->enabled) {
c = clk_get_rate(imx->clk_per);
c *= state->period;
do_div(c, 1000000000);
period_cycles = c;
prescale = period_cycles / 0x10000 + 1;
period_cycles /= prescale;
c = (unsigned long long)period_cycles * state->duty_cycle;
do_div(c, state->period);
duty_cycles = c;
/*
* according to imx pwm RM, the real period value should be
* PERIOD value in PWMPR plus 2.
*/
if (period_cycles > 2)
period_cycles -= 2;
else
period_cycles = 0;
/*
* Wait for a free FIFO slot if the PWM is already enabled, and
* flush the FIFO if the PWM was disabled and is about to be
* enabled.
*/
if (cstate.enabled) {
imx_pwm_wait_fifo_slot(chip, pwm);
} else {
ret = clk_prepare_enable(imx->clk_per);
if (ret)
return ret;
imx_pwm_sw_reset(chip);
}
ret = clk_prepare_enable(imx->clk_per); writel(duty_cycles, imx->mmio_base + MX3_PWMSAR);
if (ret) writel(period_cycles, imx->mmio_base + MX3_PWMPR);
return ret;
imx->set_enable(chip, true); cr = MX3_PWMCR_PRESCALER(prescale) |
MX3_PWMCR_DOZEEN | MX3_PWMCR_WAITEN |
MX3_PWMCR_DBGEN | MX3_PWMCR_CLKSRC_IPG_HIGH |
MX3_PWMCR_EN;
return 0; if (state->polarity == PWM_POLARITY_INVERSED)
} cr |= MX3_PWMCR_POUTC;
static void imx_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm) writel(cr, imx->mmio_base + MX3_PWMCR);
{ } else if (cstate.enabled) {
struct imx_chip *imx = to_imx_chip(chip); writel(0, imx->mmio_base + MX3_PWMCR);
imx->set_enable(chip, false); clk_disable_unprepare(imx->clk_per);
}
clk_disable_unprepare(imx->clk_per); return 0;
} }
static struct pwm_ops imx_pwm_ops = { static const struct pwm_ops imx_pwm_ops_v1 = {
.enable = imx_pwm_enable, .enable = imx_pwm_enable_v1,
.disable = imx_pwm_disable, .disable = imx_pwm_disable_v1,
.config = imx_pwm_config, .config = imx_pwm_config_v1,
.owner = THIS_MODULE,
};
static const struct pwm_ops imx_pwm_ops_v2 = {
.apply = imx_pwm_apply_v2,
.owner = THIS_MODULE, .owner = THIS_MODULE,
}; };
struct imx_pwm_data { struct imx_pwm_data {
int (*config)(struct pwm_chip *chip, bool polarity_supported;
struct pwm_device *pwm, int duty_ns, int period_ns); const struct pwm_ops *ops;
void (*set_enable)(struct pwm_chip *chip, bool enable);
}; };
static struct imx_pwm_data imx_pwm_data_v1 = { static struct imx_pwm_data imx_pwm_data_v1 = {
.config = imx_pwm_config_v1, .ops = &imx_pwm_ops_v1,
.set_enable = imx_pwm_set_enable_v1,
}; };
static struct imx_pwm_data imx_pwm_data_v2 = { static struct imx_pwm_data imx_pwm_data_v2 = {
.config = imx_pwm_config_v2, .polarity_supported = true,
.set_enable = imx_pwm_set_enable_v2, .ops = &imx_pwm_ops_v2,
}; };
static const struct of_device_id imx_pwm_dt_ids[] = { static const struct of_device_id imx_pwm_dt_ids[] = {
...@@ -282,6 +272,8 @@ static int imx_pwm_probe(struct platform_device *pdev) ...@@ -282,6 +272,8 @@ static int imx_pwm_probe(struct platform_device *pdev)
if (!of_id) if (!of_id)
return -ENODEV; return -ENODEV;
data = of_id->data;
imx = devm_kzalloc(&pdev->dev, sizeof(*imx), GFP_KERNEL); imx = devm_kzalloc(&pdev->dev, sizeof(*imx), GFP_KERNEL);
if (imx == NULL) if (imx == NULL)
return -ENOMEM; return -ENOMEM;
...@@ -293,27 +285,22 @@ static int imx_pwm_probe(struct platform_device *pdev) ...@@ -293,27 +285,22 @@ static int imx_pwm_probe(struct platform_device *pdev)
return PTR_ERR(imx->clk_per); return PTR_ERR(imx->clk_per);
} }
imx->clk_ipg = devm_clk_get(&pdev->dev, "ipg"); imx->chip.ops = data->ops;
if (IS_ERR(imx->clk_ipg)) {
dev_err(&pdev->dev, "getting ipg clock failed with %ld\n",
PTR_ERR(imx->clk_ipg));
return PTR_ERR(imx->clk_ipg);
}
imx->chip.ops = &imx_pwm_ops;
imx->chip.dev = &pdev->dev; imx->chip.dev = &pdev->dev;
imx->chip.base = -1; imx->chip.base = -1;
imx->chip.npwm = 1; imx->chip.npwm = 1;
if (data->polarity_supported) {
dev_dbg(&pdev->dev, "PWM supports output inversion\n");
imx->chip.of_xlate = of_pwm_xlate_with_flags;
imx->chip.of_pwm_n_cells = 3;
}
r = platform_get_resource(pdev, IORESOURCE_MEM, 0); r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
imx->mmio_base = devm_ioremap_resource(&pdev->dev, r); imx->mmio_base = devm_ioremap_resource(&pdev->dev, r);
if (IS_ERR(imx->mmio_base)) if (IS_ERR(imx->mmio_base))
return PTR_ERR(imx->mmio_base); return PTR_ERR(imx->mmio_base);
data = of_id->data;
imx->config = data->config;
imx->set_enable = data->set_enable;
ret = pwmchip_add(&imx->chip); ret = pwmchip_add(&imx->chip);
if (ret < 0) if (ret < 0)
return ret; return ret;
......
...@@ -17,6 +17,27 @@ ...@@ -17,6 +17,27 @@
#include "pwm-lpss.h" #include "pwm-lpss.h"
/* BayTrail */
static const struct pwm_lpss_boardinfo pwm_lpss_byt_info = {
.clk_rate = 25000000,
.npwm = 1,
.base_unit_bits = 16,
};
/* Braswell */
static const struct pwm_lpss_boardinfo pwm_lpss_bsw_info = {
.clk_rate = 19200000,
.npwm = 1,
.base_unit_bits = 16,
};
/* Broxton */
static const struct pwm_lpss_boardinfo pwm_lpss_bxt_info = {
.clk_rate = 19200000,
.npwm = 4,
.base_unit_bits = 22,
};
static int pwm_lpss_probe_pci(struct pci_dev *pdev, static int pwm_lpss_probe_pci(struct pci_dev *pdev,
const struct pci_device_id *id) const struct pci_device_id *id)
{ {
...@@ -80,6 +101,7 @@ static const struct pci_device_id pwm_lpss_pci_ids[] = { ...@@ -80,6 +101,7 @@ static const struct pci_device_id pwm_lpss_pci_ids[] = {
{ PCI_VDEVICE(INTEL, 0x1ac8), (unsigned long)&pwm_lpss_bxt_info}, { PCI_VDEVICE(INTEL, 0x1ac8), (unsigned long)&pwm_lpss_bxt_info},
{ PCI_VDEVICE(INTEL, 0x2288), (unsigned long)&pwm_lpss_bsw_info}, { PCI_VDEVICE(INTEL, 0x2288), (unsigned long)&pwm_lpss_bsw_info},
{ PCI_VDEVICE(INTEL, 0x2289), (unsigned long)&pwm_lpss_bsw_info}, { PCI_VDEVICE(INTEL, 0x2289), (unsigned long)&pwm_lpss_bsw_info},
{ PCI_VDEVICE(INTEL, 0x31c8), (unsigned long)&pwm_lpss_bxt_info},
{ PCI_VDEVICE(INTEL, 0x5ac8), (unsigned long)&pwm_lpss_bxt_info}, { PCI_VDEVICE(INTEL, 0x5ac8), (unsigned long)&pwm_lpss_bxt_info},
{ }, { },
}; };
......
...@@ -18,6 +18,27 @@ ...@@ -18,6 +18,27 @@
#include "pwm-lpss.h" #include "pwm-lpss.h"
/* BayTrail */
static const struct pwm_lpss_boardinfo pwm_lpss_byt_info = {
.clk_rate = 25000000,
.npwm = 1,
.base_unit_bits = 16,
};
/* Braswell */
static const struct pwm_lpss_boardinfo pwm_lpss_bsw_info = {
.clk_rate = 19200000,
.npwm = 1,
.base_unit_bits = 16,
};
/* Broxton */
static const struct pwm_lpss_boardinfo pwm_lpss_bxt_info = {
.clk_rate = 19200000,
.npwm = 4,
.base_unit_bits = 22,
};
static int pwm_lpss_probe_platform(struct platform_device *pdev) static int pwm_lpss_probe_platform(struct platform_device *pdev)
{ {
const struct pwm_lpss_boardinfo *info; const struct pwm_lpss_boardinfo *info;
......
...@@ -15,6 +15,7 @@ ...@@ -15,6 +15,7 @@
#include <linux/delay.h> #include <linux/delay.h>
#include <linux/io.h> #include <linux/io.h>
#include <linux/iopoll.h>
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/pm_runtime.h> #include <linux/pm_runtime.h>
...@@ -37,30 +38,6 @@ struct pwm_lpss_chip { ...@@ -37,30 +38,6 @@ struct pwm_lpss_chip {
const struct pwm_lpss_boardinfo *info; const struct pwm_lpss_boardinfo *info;
}; };
/* BayTrail */
const struct pwm_lpss_boardinfo pwm_lpss_byt_info = {
.clk_rate = 25000000,
.npwm = 1,
.base_unit_bits = 16,
};
EXPORT_SYMBOL_GPL(pwm_lpss_byt_info);
/* Braswell */
const struct pwm_lpss_boardinfo pwm_lpss_bsw_info = {
.clk_rate = 19200000,
.npwm = 1,
.base_unit_bits = 16,
};
EXPORT_SYMBOL_GPL(pwm_lpss_bsw_info);
/* Broxton */
const struct pwm_lpss_boardinfo pwm_lpss_bxt_info = {
.clk_rate = 19200000,
.npwm = 4,
.base_unit_bits = 22,
};
EXPORT_SYMBOL_GPL(pwm_lpss_bxt_info);
static inline struct pwm_lpss_chip *to_lpwm(struct pwm_chip *chip) static inline struct pwm_lpss_chip *to_lpwm(struct pwm_chip *chip)
{ {
return container_of(chip, struct pwm_lpss_chip, chip); return container_of(chip, struct pwm_lpss_chip, chip);
...@@ -80,17 +57,42 @@ static inline void pwm_lpss_write(const struct pwm_device *pwm, u32 value) ...@@ -80,17 +57,42 @@ static inline void pwm_lpss_write(const struct pwm_device *pwm, u32 value)
writel(value, lpwm->regs + pwm->hwpwm * PWM_SIZE + PWM); writel(value, lpwm->regs + pwm->hwpwm * PWM_SIZE + PWM);
} }
static void pwm_lpss_update(struct pwm_device *pwm) static int pwm_lpss_update(struct pwm_device *pwm)
{ {
struct pwm_lpss_chip *lpwm = to_lpwm(pwm->chip);
const void __iomem *addr = lpwm->regs + pwm->hwpwm * PWM_SIZE + PWM;
const unsigned int ms = 500 * USEC_PER_MSEC;
u32 val;
int err;
pwm_lpss_write(pwm, pwm_lpss_read(pwm) | PWM_SW_UPDATE); pwm_lpss_write(pwm, pwm_lpss_read(pwm) | PWM_SW_UPDATE);
/* Give it some time to propagate */
usleep_range(10, 50); /*
* PWM Configuration register has SW_UPDATE bit that is set when a new
* configuration is written to the register. The bit is automatically
* cleared at the start of the next output cycle by the IP block.
*
* If one writes a new configuration to the register while it still has
* the bit enabled, PWM may freeze. That is, while one can still write
* to the register, it won't have an effect. Thus, we try to sleep long
* enough that the bit gets cleared and make sure the bit is not
* enabled while we update the configuration.
*/
err = readl_poll_timeout(addr, val, !(val & PWM_SW_UPDATE), 40, ms);
if (err)
dev_err(pwm->chip->dev, "PWM_SW_UPDATE was not cleared\n");
return err;
} }
static int pwm_lpss_config(struct pwm_chip *chip, struct pwm_device *pwm, static inline int pwm_lpss_is_updating(struct pwm_device *pwm)
int duty_ns, int period_ns) {
return (pwm_lpss_read(pwm) & PWM_SW_UPDATE) ? -EBUSY : 0;
}
static void pwm_lpss_prepare(struct pwm_lpss_chip *lpwm, struct pwm_device *pwm,
int duty_ns, int period_ns)
{ {
struct pwm_lpss_chip *lpwm = to_lpwm(chip);
unsigned long long on_time_div; unsigned long long on_time_div;
unsigned long c = lpwm->info->clk_rate, base_unit_range; unsigned long c = lpwm->info->clk_rate, base_unit_range;
unsigned long long base_unit, freq = NSEC_PER_SEC; unsigned long long base_unit, freq = NSEC_PER_SEC;
...@@ -102,62 +104,62 @@ static int pwm_lpss_config(struct pwm_chip *chip, struct pwm_device *pwm, ...@@ -102,62 +104,62 @@ static int pwm_lpss_config(struct pwm_chip *chip, struct pwm_device *pwm,
* The equation is: * The equation is:
* base_unit = round(base_unit_range * freq / c) * base_unit = round(base_unit_range * freq / c)
*/ */
base_unit_range = BIT(lpwm->info->base_unit_bits); base_unit_range = BIT(lpwm->info->base_unit_bits) - 1;
freq *= base_unit_range; freq *= base_unit_range;
base_unit = DIV_ROUND_CLOSEST_ULL(freq, c); base_unit = DIV_ROUND_CLOSEST_ULL(freq, c);
if (duty_ns <= 0)
duty_ns = 1;
on_time_div = 255ULL * duty_ns; on_time_div = 255ULL * duty_ns;
do_div(on_time_div, period_ns); do_div(on_time_div, period_ns);
on_time_div = 255ULL - on_time_div; on_time_div = 255ULL - on_time_div;
pm_runtime_get_sync(chip->dev);
ctrl = pwm_lpss_read(pwm); ctrl = pwm_lpss_read(pwm);
ctrl &= ~PWM_ON_TIME_DIV_MASK; ctrl &= ~PWM_ON_TIME_DIV_MASK;
ctrl &= ~((base_unit_range - 1) << PWM_BASE_UNIT_SHIFT); ctrl &= ~(base_unit_range << PWM_BASE_UNIT_SHIFT);
base_unit &= (base_unit_range - 1); base_unit &= base_unit_range;
ctrl |= (u32) base_unit << PWM_BASE_UNIT_SHIFT; ctrl |= (u32) base_unit << PWM_BASE_UNIT_SHIFT;
ctrl |= on_time_div; ctrl |= on_time_div;
pwm_lpss_write(pwm, ctrl); pwm_lpss_write(pwm, ctrl);
/*
* If the PWM is already enabled we need to notify the hardware
* about the change by setting PWM_SW_UPDATE.
*/
if (pwm_is_enabled(pwm))
pwm_lpss_update(pwm);
pm_runtime_put(chip->dev);
return 0;
} }
static int pwm_lpss_enable(struct pwm_chip *chip, struct pwm_device *pwm) static int pwm_lpss_apply(struct pwm_chip *chip, struct pwm_device *pwm,
struct pwm_state *state)
{ {
pm_runtime_get_sync(chip->dev); struct pwm_lpss_chip *lpwm = to_lpwm(chip);
int ret;
/* if (state->enabled) {
* Hardware must first see PWM_SW_UPDATE before the PWM can be if (!pwm_is_enabled(pwm)) {
* enabled. pm_runtime_get_sync(chip->dev);
*/ ret = pwm_lpss_is_updating(pwm);
pwm_lpss_update(pwm); if (ret) {
pwm_lpss_write(pwm, pwm_lpss_read(pwm) | PWM_ENABLE); pm_runtime_put(chip->dev);
return 0; return ret;
} }
pwm_lpss_prepare(lpwm, pwm, state->duty_cycle, state->period);
ret = pwm_lpss_update(pwm);
if (ret) {
pm_runtime_put(chip->dev);
return ret;
}
pwm_lpss_write(pwm, pwm_lpss_read(pwm) | PWM_ENABLE);
} else {
ret = pwm_lpss_is_updating(pwm);
if (ret)
return ret;
pwm_lpss_prepare(lpwm, pwm, state->duty_cycle, state->period);
return pwm_lpss_update(pwm);
}
} else if (pwm_is_enabled(pwm)) {
pwm_lpss_write(pwm, pwm_lpss_read(pwm) & ~PWM_ENABLE);
pm_runtime_put(chip->dev);
}
static void pwm_lpss_disable(struct pwm_chip *chip, struct pwm_device *pwm) return 0;
{
pwm_lpss_write(pwm, pwm_lpss_read(pwm) & ~PWM_ENABLE);
pm_runtime_put(chip->dev);
} }
static const struct pwm_ops pwm_lpss_ops = { static const struct pwm_ops pwm_lpss_ops = {
.config = pwm_lpss_config, .apply = pwm_lpss_apply,
.enable = pwm_lpss_enable,
.disable = pwm_lpss_disable,
.owner = THIS_MODULE, .owner = THIS_MODULE,
}; };
......
...@@ -24,10 +24,6 @@ struct pwm_lpss_boardinfo { ...@@ -24,10 +24,6 @@ struct pwm_lpss_boardinfo {
unsigned long base_unit_bits; unsigned long base_unit_bits;
}; };
extern const struct pwm_lpss_boardinfo pwm_lpss_byt_info;
extern const struct pwm_lpss_boardinfo pwm_lpss_bsw_info;
extern const struct pwm_lpss_boardinfo pwm_lpss_bxt_info;
struct pwm_lpss_chip *pwm_lpss_probe(struct device *dev, struct resource *r, struct pwm_lpss_chip *pwm_lpss_probe(struct device *dev, struct resource *r,
const struct pwm_lpss_boardinfo *info); const struct pwm_lpss_boardinfo *info);
int pwm_lpss_remove(struct pwm_lpss_chip *lpwm); int pwm_lpss_remove(struct pwm_lpss_chip *lpwm);
......
...@@ -20,8 +20,10 @@ ...@@ -20,8 +20,10 @@
*/ */
#include <linux/acpi.h> #include <linux/acpi.h>
#include <linux/gpio/driver.h>
#include <linux/i2c.h> #include <linux/i2c.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/mutex.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/property.h> #include <linux/property.h>
#include <linux/pwm.h> #include <linux/pwm.h>
...@@ -65,7 +67,6 @@ ...@@ -65,7 +67,6 @@
#define PCA9685_MAXCHAN 0x10 #define PCA9685_MAXCHAN 0x10
#define LED_FULL (1 << 4) #define LED_FULL (1 << 4)
#define MODE1_RESTART (1 << 7)
#define MODE1_SLEEP (1 << 4) #define MODE1_SLEEP (1 << 4)
#define MODE2_INVRT (1 << 4) #define MODE2_INVRT (1 << 4)
#define MODE2_OUTDRV (1 << 2) #define MODE2_OUTDRV (1 << 2)
...@@ -81,6 +82,10 @@ struct pca9685 { ...@@ -81,6 +82,10 @@ struct pca9685 {
int active_cnt; int active_cnt;
int duty_ns; int duty_ns;
int period_ns; int period_ns;
#if IS_ENABLED(CONFIG_GPIOLIB)
struct mutex lock;
struct gpio_chip gpio;
#endif
}; };
static inline struct pca9685 *to_pca(struct pwm_chip *chip) static inline struct pca9685 *to_pca(struct pwm_chip *chip)
...@@ -88,6 +93,151 @@ static inline struct pca9685 *to_pca(struct pwm_chip *chip) ...@@ -88,6 +93,151 @@ static inline struct pca9685 *to_pca(struct pwm_chip *chip)
return container_of(chip, struct pca9685, chip); return container_of(chip, struct pca9685, chip);
} }
#if IS_ENABLED(CONFIG_GPIOLIB)
static int pca9685_pwm_gpio_request(struct gpio_chip *gpio, unsigned int offset)
{
struct pca9685 *pca = gpiochip_get_data(gpio);
struct pwm_device *pwm;
mutex_lock(&pca->lock);
pwm = &pca->chip.pwms[offset];
if (pwm->flags & (PWMF_REQUESTED | PWMF_EXPORTED)) {
mutex_unlock(&pca->lock);
return -EBUSY;
}
pwm_set_chip_data(pwm, (void *)1);
mutex_unlock(&pca->lock);
return 0;
}
static void pca9685_pwm_gpio_free(struct gpio_chip *gpio, unsigned int offset)
{
struct pca9685 *pca = gpiochip_get_data(gpio);
struct pwm_device *pwm;
mutex_lock(&pca->lock);
pwm = &pca->chip.pwms[offset];
pwm_set_chip_data(pwm, NULL);
mutex_unlock(&pca->lock);
}
static bool pca9685_pwm_is_gpio(struct pca9685 *pca, struct pwm_device *pwm)
{
bool is_gpio = false;
mutex_lock(&pca->lock);
if (pwm->hwpwm >= PCA9685_MAXCHAN) {
unsigned int i;
/*
* Check if any of the GPIOs are requested and in that case
* prevent using the "all LEDs" channel.
*/
for (i = 0; i < pca->gpio.ngpio; i++)
if (gpiochip_is_requested(&pca->gpio, i)) {
is_gpio = true;
break;
}
} else if (pwm_get_chip_data(pwm)) {
is_gpio = true;
}
mutex_unlock(&pca->lock);
return is_gpio;
}
static int pca9685_pwm_gpio_get(struct gpio_chip *gpio, unsigned int offset)
{
struct pca9685 *pca = gpiochip_get_data(gpio);
struct pwm_device *pwm = &pca->chip.pwms[offset];
unsigned int value;
regmap_read(pca->regmap, LED_N_ON_H(pwm->hwpwm), &value);
return value & LED_FULL;
}
static void pca9685_pwm_gpio_set(struct gpio_chip *gpio, unsigned int offset,
int value)
{
struct pca9685 *pca = gpiochip_get_data(gpio);
struct pwm_device *pwm = &pca->chip.pwms[offset];
unsigned int on = value ? LED_FULL : 0;
/* Clear both OFF registers */
regmap_write(pca->regmap, LED_N_OFF_L(pwm->hwpwm), 0);
regmap_write(pca->regmap, LED_N_OFF_H(pwm->hwpwm), 0);
/* Set the full ON bit */
regmap_write(pca->regmap, LED_N_ON_H(pwm->hwpwm), on);
}
static int pca9685_pwm_gpio_get_direction(struct gpio_chip *chip,
unsigned int offset)
{
/* Always out */
return 0;
}
static int pca9685_pwm_gpio_direction_input(struct gpio_chip *gpio,
unsigned int offset)
{
return -EINVAL;
}
static int pca9685_pwm_gpio_direction_output(struct gpio_chip *gpio,
unsigned int offset, int value)
{
pca9685_pwm_gpio_set(gpio, offset, value);
return 0;
}
/*
* The PCA9685 has a bit for turning the PWM output full off or on. Some
* boards like Intel Galileo actually uses these as normal GPIOs so we
* expose a GPIO chip here which can exclusively take over the underlying
* PWM channel.
*/
static int pca9685_pwm_gpio_probe(struct pca9685 *pca)
{
struct device *dev = pca->chip.dev;
mutex_init(&pca->lock);
pca->gpio.label = dev_name(dev);
pca->gpio.parent = dev;
pca->gpio.request = pca9685_pwm_gpio_request;
pca->gpio.free = pca9685_pwm_gpio_free;
pca->gpio.get_direction = pca9685_pwm_gpio_get_direction;
pca->gpio.direction_input = pca9685_pwm_gpio_direction_input;
pca->gpio.direction_output = pca9685_pwm_gpio_direction_output;
pca->gpio.get = pca9685_pwm_gpio_get;
pca->gpio.set = pca9685_pwm_gpio_set;
pca->gpio.base = -1;
pca->gpio.ngpio = PCA9685_MAXCHAN;
pca->gpio.can_sleep = true;
return devm_gpiochip_add_data(dev, &pca->gpio, pca);
}
#else
static inline bool pca9685_pwm_is_gpio(struct pca9685 *pca,
struct pwm_device *pwm)
{
return false;
}
static inline int pca9685_pwm_gpio_probe(struct pca9685 *pca)
{
return 0;
}
#endif
static int pca9685_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, static int pca9685_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
int duty_ns, int period_ns) int duty_ns, int period_ns)
{ {
...@@ -117,16 +267,6 @@ static int pca9685_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, ...@@ -117,16 +267,6 @@ static int pca9685_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
udelay(500); udelay(500);
pca->period_ns = period_ns; pca->period_ns = period_ns;
/*
* If the duty cycle did not change, restart PWM with
* the same duty cycle to period ratio and return.
*/
if (duty_ns == pca->duty_ns) {
regmap_update_bits(pca->regmap, PCA9685_MODE1,
MODE1_RESTART, 0x1);
return 0;
}
} else { } else {
dev_err(chip->dev, dev_err(chip->dev,
"prescaler not set: period out of bounds!\n"); "prescaler not set: period out of bounds!\n");
...@@ -264,6 +404,9 @@ static int pca9685_pwm_request(struct pwm_chip *chip, struct pwm_device *pwm) ...@@ -264,6 +404,9 @@ static int pca9685_pwm_request(struct pwm_chip *chip, struct pwm_device *pwm)
{ {
struct pca9685 *pca = to_pca(chip); struct pca9685 *pca = to_pca(chip);
if (pca9685_pwm_is_gpio(pca, pwm))
return -EBUSY;
if (pca->active_cnt++ == 0) if (pca->active_cnt++ == 0)
return regmap_update_bits(pca->regmap, PCA9685_MODE1, return regmap_update_bits(pca->regmap, PCA9685_MODE1,
MODE1_SLEEP, 0x0); MODE1_SLEEP, 0x0);
...@@ -344,7 +487,15 @@ static int pca9685_pwm_probe(struct i2c_client *client, ...@@ -344,7 +487,15 @@ static int pca9685_pwm_probe(struct i2c_client *client,
pca->chip.dev = &client->dev; pca->chip.dev = &client->dev;
pca->chip.base = -1; pca->chip.base = -1;
return pwmchip_add(&pca->chip); ret = pwmchip_add(&pca->chip);
if (ret < 0)
return ret;
ret = pca9685_pwm_gpio_probe(pca);
if (ret < 0)
pwmchip_remove(&pca->chip);
return ret;
} }
static int pca9685_pwm_remove(struct i2c_client *client) static int pca9685_pwm_remove(struct i2c_client *client)
......
...@@ -118,7 +118,7 @@ static void pxa_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm) ...@@ -118,7 +118,7 @@ static void pxa_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm)
clk_disable_unprepare(pc->clk); clk_disable_unprepare(pc->clk);
} }
static struct pwm_ops pxa_pwm_ops = { static const struct pwm_ops pxa_pwm_ops = {
.config = pxa_pwm_config, .config = pxa_pwm_config,
.enable = pxa_pwm_enable, .enable = pxa_pwm_enable,
.disable = pxa_pwm_disable, .disable = pxa_pwm_disable,
......
...@@ -184,7 +184,7 @@ static int vt8500_pwm_set_polarity(struct pwm_chip *chip, ...@@ -184,7 +184,7 @@ static int vt8500_pwm_set_polarity(struct pwm_chip *chip,
return 0; return 0;
} }
static struct pwm_ops vt8500_pwm_ops = { static const struct pwm_ops vt8500_pwm_ops = {
.enable = vt8500_pwm_enable, .enable = vt8500_pwm_enable,
.disable = vt8500_pwm_disable, .disable = vt8500_pwm_disable,
.config = vt8500_pwm_config, .config = vt8500_pwm_config,
......
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