diff options
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/pwm/Kconfig | 17 | ||||
-rw-r--r-- | drivers/pwm/Makefile | 3 | ||||
-rw-r--r-- | drivers/pwm/core.c | 10 | ||||
-rw-r--r-- | drivers/pwm/pwm-atmel.c | 111 | ||||
-rw-r--r-- | drivers/pwm/pwm-bcm-kona.c | 16 | ||||
-rw-r--r-- | drivers/pwm/pwm-hibvt.c | 44 | ||||
-rw-r--r-- | drivers/pwm/pwm-imx1.c | 199 | ||||
-rw-r--r-- | drivers/pwm/pwm-imx27.c (renamed from drivers/pwm/pwm-imx.c) | 224 | ||||
-rw-r--r-- | drivers/pwm/pwm-mtk-disp.c | 11 | ||||
-rw-r--r-- | drivers/pwm/pwm-rcar.c | 99 |
10 files changed, 453 insertions, 281 deletions
diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig index a8f47df0655a..54f8238aac0d 100644 --- a/drivers/pwm/Kconfig +++ b/drivers/pwm/Kconfig @@ -192,14 +192,23 @@ config PWM_IMG To compile this driver as a module, choose M here: the module will be called pwm-img -config PWM_IMX - tristate "i.MX PWM support" +config PWM_IMX1 + tristate "i.MX1 PWM support" depends on ARCH_MXC help - Generic PWM framework driver for i.MX. + Generic PWM framework driver for i.MX1 and i.MX21 To compile this driver as a module, choose M here: the module - will be called pwm-imx. + will be called pwm-imx1. + +config PWM_IMX27 + tristate "i.MX27 PWM support" + depends on ARCH_MXC + help + Generic PWM framework driver for i.MX27 and later i.MX SoCs. + + To compile this driver as a module, choose M here: the module + will be called pwm-imx27. config PWM_JZ4740 tristate "Ingenic JZ47xx PWM support" diff --git a/drivers/pwm/Makefile b/drivers/pwm/Makefile index 9c676a0dadf5..448825e892bc 100644 --- a/drivers/pwm/Makefile +++ b/drivers/pwm/Makefile @@ -17,7 +17,8 @@ obj-$(CONFIG_PWM_EP93XX) += pwm-ep93xx.o obj-$(CONFIG_PWM_FSL_FTM) += pwm-fsl-ftm.o obj-$(CONFIG_PWM_HIBVT) += pwm-hibvt.o obj-$(CONFIG_PWM_IMG) += pwm-img.o -obj-$(CONFIG_PWM_IMX) += pwm-imx.o +obj-$(CONFIG_PWM_IMX1) += pwm-imx1.o +obj-$(CONFIG_PWM_IMX27) += pwm-imx27.o obj-$(CONFIG_PWM_JZ4740) += pwm-jz4740.o obj-$(CONFIG_PWM_LP3943) += pwm-lp3943.o obj-$(CONFIG_PWM_LPC18XX_SCT) += pwm-lpc18xx-sct.o diff --git a/drivers/pwm/core.c b/drivers/pwm/core.c index 1581f6ab1b1f..3149204567f3 100644 --- a/drivers/pwm/core.c +++ b/drivers/pwm/core.c @@ -472,7 +472,10 @@ int pwm_apply_state(struct pwm_device *pwm, struct pwm_state *state) state->duty_cycle > state->period) return -EINVAL; - if (!memcmp(state, &pwm->state, sizeof(*state))) + if (state->period == pwm->state.period && + state->duty_cycle == pwm->state.duty_cycle && + state->polarity == pwm->state.polarity && + state->enabled == pwm->state.enabled) return 0; if (pwm->chip->ops->apply) { @@ -1033,10 +1036,7 @@ static int pwm_seq_show(struct seq_file *s, void *v) dev_name(chip->dev), chip->npwm, (chip->npwm != 1) ? "s" : ""); - if (chip->ops->dbg_show) - chip->ops->dbg_show(chip, s); - else - pwm_dbg_show(chip, s); + pwm_dbg_show(chip, s); return 0; } diff --git a/drivers/pwm/pwm-atmel.c b/drivers/pwm/pwm-atmel.c index 530d7dc5f1b5..a9fd6f0d408c 100644 --- a/drivers/pwm/pwm-atmel.c +++ b/drivers/pwm/pwm-atmel.c @@ -48,16 +48,6 @@ #define PWMV2_CPRD 0x0C #define PWMV2_CPRDUPD 0x10 -/* - * Max value for duty and period - * - * Although the duty and period register is 32 bit, - * however only the LSB 16 bits are significant. - */ -#define PWM_MAX_DTY 0xFFFF -#define PWM_MAX_PRD 0xFFFF -#define PRD_MAX_PRES 10 - struct atmel_pwm_registers { u8 period; u8 period_upd; @@ -65,11 +55,21 @@ struct atmel_pwm_registers { u8 duty_upd; }; +struct atmel_pwm_config { + u32 max_period; + u32 max_pres; +}; + +struct atmel_pwm_data { + struct atmel_pwm_registers regs; + struct atmel_pwm_config cfg; +}; + struct atmel_pwm_chip { struct pwm_chip chip; struct clk *clk; void __iomem *base; - const struct atmel_pwm_registers *regs; + const struct atmel_pwm_data *data; unsigned int updated_pwms; /* ISR is cleared when read, ensure only one thread does that */ @@ -121,10 +121,10 @@ static int atmel_pwm_calculate_cprd_and_pres(struct pwm_chip *chip, cycles *= clk_get_rate(atmel_pwm->clk); do_div(cycles, NSEC_PER_SEC); - for (*pres = 0; cycles > PWM_MAX_PRD; cycles >>= 1) + for (*pres = 0; cycles > atmel_pwm->data->cfg.max_period; cycles >>= 1) (*pres)++; - if (*pres > PRD_MAX_PRES) { + if (*pres > atmel_pwm->data->cfg.max_pres) { dev_err(chip->dev, "pres exceeds the maximum value\n"); return -EINVAL; } @@ -150,15 +150,15 @@ static void atmel_pwm_update_cdty(struct pwm_chip *chip, struct pwm_device *pwm, struct atmel_pwm_chip *atmel_pwm = to_atmel_pwm_chip(chip); u32 val; - if (atmel_pwm->regs->duty_upd == - atmel_pwm->regs->period_upd) { + if (atmel_pwm->data->regs.duty_upd == + atmel_pwm->data->regs.period_upd) { val = atmel_pwm_ch_readl(atmel_pwm, pwm->hwpwm, PWM_CMR); val &= ~PWM_CMR_UPD_CDTY; atmel_pwm_ch_writel(atmel_pwm, pwm->hwpwm, PWM_CMR, val); } atmel_pwm_ch_writel(atmel_pwm, pwm->hwpwm, - atmel_pwm->regs->duty_upd, cdty); + atmel_pwm->data->regs.duty_upd, cdty); } static void atmel_pwm_set_cprd_cdty(struct pwm_chip *chip, @@ -168,9 +168,9 @@ static void atmel_pwm_set_cprd_cdty(struct pwm_chip *chip, struct atmel_pwm_chip *atmel_pwm = to_atmel_pwm_chip(chip); atmel_pwm_ch_writel(atmel_pwm, pwm->hwpwm, - atmel_pwm->regs->duty, cdty); + atmel_pwm->data->regs.duty, cdty); atmel_pwm_ch_writel(atmel_pwm, pwm->hwpwm, - atmel_pwm->regs->period, cprd); + atmel_pwm->data->regs.period, cprd); } static void atmel_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm, @@ -225,7 +225,7 @@ static int atmel_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm, cstate.polarity == state->polarity && cstate.period == state->period) { cprd = atmel_pwm_ch_readl(atmel_pwm, pwm->hwpwm, - atmel_pwm->regs->period); + atmel_pwm->data->regs.period); atmel_pwm_calculate_cdty(state, cprd, &cdty); atmel_pwm_update_cdty(chip, pwm, cdty); return 0; @@ -277,27 +277,55 @@ static const struct pwm_ops atmel_pwm_ops = { .owner = THIS_MODULE, }; -static const struct atmel_pwm_registers atmel_pwm_regs_v1 = { - .period = PWMV1_CPRD, - .period_upd = PWMV1_CUPD, - .duty = PWMV1_CDTY, - .duty_upd = PWMV1_CUPD, +static const struct atmel_pwm_data atmel_sam9rl_pwm_data = { + .regs = { + .period = PWMV1_CPRD, + .period_upd = PWMV1_CUPD, + .duty = PWMV1_CDTY, + .duty_upd = PWMV1_CUPD, + }, + .cfg = { + /* 16 bits to keep period and duty. */ + .max_period = 0xffff, + .max_pres = 10, + }, +}; + +static const struct atmel_pwm_data atmel_sama5_pwm_data = { + .regs = { + .period = PWMV2_CPRD, + .period_upd = PWMV2_CPRDUPD, + .duty = PWMV2_CDTY, + .duty_upd = PWMV2_CDTYUPD, + }, + .cfg = { + /* 16 bits to keep period and duty. */ + .max_period = 0xffff, + .max_pres = 10, + }, }; -static const struct atmel_pwm_registers atmel_pwm_regs_v2 = { - .period = PWMV2_CPRD, - .period_upd = PWMV2_CPRDUPD, - .duty = PWMV2_CDTY, - .duty_upd = PWMV2_CDTYUPD, +static const struct atmel_pwm_data mchp_sam9x60_pwm_data = { + .regs = { + .period = PWMV1_CPRD, + .period_upd = PWMV1_CUPD, + .duty = PWMV1_CDTY, + .duty_upd = PWMV1_CUPD, + }, + .cfg = { + /* 32 bits to keep period and duty. */ + .max_period = 0xffffffff, + .max_pres = 10, + }, }; static const struct platform_device_id atmel_pwm_devtypes[] = { { .name = "at91sam9rl-pwm", - .driver_data = (kernel_ulong_t)&atmel_pwm_regs_v1, + .driver_data = (kernel_ulong_t)&atmel_sam9rl_pwm_data, }, { .name = "sama5d3-pwm", - .driver_data = (kernel_ulong_t)&atmel_pwm_regs_v2, + .driver_data = (kernel_ulong_t)&atmel_sama5_pwm_data, }, { /* sentinel */ }, @@ -307,20 +335,23 @@ MODULE_DEVICE_TABLE(platform, atmel_pwm_devtypes); static const struct of_device_id atmel_pwm_dt_ids[] = { { .compatible = "atmel,at91sam9rl-pwm", - .data = &atmel_pwm_regs_v1, + .data = &atmel_sam9rl_pwm_data, }, { .compatible = "atmel,sama5d3-pwm", - .data = &atmel_pwm_regs_v2, + .data = &atmel_sama5_pwm_data, }, { .compatible = "atmel,sama5d2-pwm", - .data = &atmel_pwm_regs_v2, + .data = &atmel_sama5_pwm_data, + }, { + .compatible = "microchip,sam9x60-pwm", + .data = &mchp_sam9x60_pwm_data, }, { /* sentinel */ }, }; MODULE_DEVICE_TABLE(of, atmel_pwm_dt_ids); -static inline const struct atmel_pwm_registers * +static inline const struct atmel_pwm_data * atmel_pwm_get_driver_data(struct platform_device *pdev) { const struct platform_device_id *id; @@ -330,18 +361,18 @@ atmel_pwm_get_driver_data(struct platform_device *pdev) id = platform_get_device_id(pdev); - return (struct atmel_pwm_registers *)id->driver_data; + return (struct atmel_pwm_data *)id->driver_data; } static int atmel_pwm_probe(struct platform_device *pdev) { - const struct atmel_pwm_registers *regs; + const struct atmel_pwm_data *data; struct atmel_pwm_chip *atmel_pwm; struct resource *res; int ret; - regs = atmel_pwm_get_driver_data(pdev); - if (!regs) + data = atmel_pwm_get_driver_data(pdev); + if (!data) return -ENODEV; atmel_pwm = devm_kzalloc(&pdev->dev, sizeof(*atmel_pwm), GFP_KERNEL); @@ -373,7 +404,7 @@ static int atmel_pwm_probe(struct platform_device *pdev) atmel_pwm->chip.base = -1; atmel_pwm->chip.npwm = 4; - atmel_pwm->regs = regs; + atmel_pwm->data = data; atmel_pwm->updated_pwms = 0; mutex_init(&atmel_pwm->isr_lock); diff --git a/drivers/pwm/pwm-bcm-kona.c b/drivers/pwm/pwm-bcm-kona.c index 09a95aeb3a70..81da91df2529 100644 --- a/drivers/pwm/pwm-bcm-kona.c +++ b/drivers/pwm/pwm-bcm-kona.c @@ -45,25 +45,25 @@ * high or low depending on its state at that exact instant. */ -#define PWM_CONTROL_OFFSET (0x00000000) +#define PWM_CONTROL_OFFSET 0x00000000 #define PWM_CONTROL_SMOOTH_SHIFT(chan) (24 + (chan)) #define PWM_CONTROL_TYPE_SHIFT(chan) (16 + (chan)) #define PWM_CONTROL_POLARITY_SHIFT(chan) (8 + (chan)) #define PWM_CONTROL_TRIGGER_SHIFT(chan) (chan) -#define PRESCALE_OFFSET (0x00000004) +#define PRESCALE_OFFSET 0x00000004 #define PRESCALE_SHIFT(chan) ((chan) << 2) #define PRESCALE_MASK(chan) (0x7 << PRESCALE_SHIFT(chan)) -#define PRESCALE_MIN (0x00000000) -#define PRESCALE_MAX (0x00000007) +#define PRESCALE_MIN 0x00000000 +#define PRESCALE_MAX 0x00000007 #define PERIOD_COUNT_OFFSET(chan) (0x00000008 + ((chan) << 3)) -#define PERIOD_COUNT_MIN (0x00000002) -#define PERIOD_COUNT_MAX (0x00ffffff) +#define PERIOD_COUNT_MIN 0x00000002 +#define PERIOD_COUNT_MAX 0x00ffffff #define DUTY_CYCLE_HIGH_OFFSET(chan) (0x0000000c + ((chan) << 3)) -#define DUTY_CYCLE_HIGH_MIN (0x00000000) -#define DUTY_CYCLE_HIGH_MAX (0x00ffffff) +#define DUTY_CYCLE_HIGH_MIN 0x00000000 +#define DUTY_CYCLE_HIGH_MAX 0x00ffffff struct kona_pwmc { struct pwm_chip chip; diff --git a/drivers/pwm/pwm-hibvt.c b/drivers/pwm/pwm-hibvt.c index 27c107e78d59..a0b09603d13d 100644 --- a/drivers/pwm/pwm-hibvt.c +++ b/drivers/pwm/pwm-hibvt.c @@ -49,15 +49,30 @@ struct hibvt_pwm_chip { struct clk *clk; void __iomem *base; struct reset_control *rstc; + const struct hibvt_pwm_soc *soc; }; struct hibvt_pwm_soc { u32 num_pwms; + bool quirk_force_enable; }; -static const struct hibvt_pwm_soc pwm_soc[2] = { - { .num_pwms = 4 }, - { .num_pwms = 8 }, +static const struct hibvt_pwm_soc hi3516cv300_soc_info = { + .num_pwms = 4, +}; + +static const struct hibvt_pwm_soc hi3519v100_soc_info = { + .num_pwms = 8, +}; + +static const struct hibvt_pwm_soc hi3559v100_shub_soc_info = { + .num_pwms = 8, + .quirk_force_enable = true, +}; + +static const struct hibvt_pwm_soc hi3559v100_soc_info = { + .num_pwms = 2, + .quirk_force_enable = true, }; static inline struct hibvt_pwm_chip *to_hibvt_pwm_chip(struct pwm_chip *chip) @@ -148,13 +163,23 @@ static void hibvt_pwm_get_state(struct pwm_chip *chip, struct pwm_device *pwm, static int hibvt_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm, struct pwm_state *state) { + struct hibvt_pwm_chip *hi_pwm_chip = to_hibvt_pwm_chip(chip); + if (state->polarity != pwm->state.polarity) hibvt_pwm_set_polarity(chip, pwm, state->polarity); if (state->period != pwm->state.period || - state->duty_cycle != pwm->state.duty_cycle) + state->duty_cycle != pwm->state.duty_cycle) { hibvt_pwm_config(chip, pwm, state->duty_cycle, state->period); + /* + * Some implementations require the PWM to be enabled twice + * each time the duty cycle is refreshed. + */ + if (hi_pwm_chip->soc->quirk_force_enable && state->enabled) + hibvt_pwm_enable(chip, pwm); + } + if (state->enabled != pwm->state.enabled) { if (state->enabled) hibvt_pwm_enable(chip, pwm); @@ -198,6 +223,7 @@ static int hibvt_pwm_probe(struct platform_device *pdev) pwm_chip->chip.npwm = soc->num_pwms; pwm_chip->chip.of_xlate = of_pwm_xlate_with_flags; pwm_chip->chip.of_pwm_n_cells = 3; + pwm_chip->soc = soc; res = platform_get_resource(pdev, IORESOURCE_MEM, 0); pwm_chip->base = devm_ioremap_resource(&pdev->dev, res); @@ -250,8 +276,14 @@ static int hibvt_pwm_remove(struct platform_device *pdev) } static const struct of_device_id hibvt_pwm_of_match[] = { - { .compatible = "hisilicon,hi3516cv300-pwm", .data = &pwm_soc[0] }, - { .compatible = "hisilicon,hi3519v100-pwm", .data = &pwm_soc[1] }, + { .compatible = "hisilicon,hi3516cv300-pwm", + .data = &hi3516cv300_soc_info }, + { .compatible = "hisilicon,hi3519v100-pwm", + .data = &hi3519v100_soc_info }, + { .compatible = "hisilicon,hi3559v100-shub-pwm", + .data = &hi3559v100_shub_soc_info }, + { .compatible = "hisilicon,hi3559v100-pwm", + .data = &hi3559v100_soc_info }, { } }; MODULE_DEVICE_TABLE(of, hibvt_pwm_of_match); diff --git a/drivers/pwm/pwm-imx1.c b/drivers/pwm/pwm-imx1.c new file mode 100644 index 000000000000..f8b2c2e001a7 --- /dev/null +++ b/drivers/pwm/pwm-imx1.c @@ -0,0 +1,199 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * simple driver for PWM (Pulse Width Modulator) controller + * + * Derived from pxa PWM driver by eric miao <eric.miao@marvell.com> + */ + +#include <linux/bitfield.h> +#include <linux/bitops.h> +#include <linux/clk.h> +#include <linux/delay.h> +#include <linux/err.h> +#include <linux/io.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/of_device.h> +#include <linux/platform_device.h> +#include <linux/pwm.h> +#include <linux/slab.h> + +#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 BIT(4) + +struct pwm_imx1_chip { + struct clk *clk_ipg; + struct clk *clk_per; + void __iomem *mmio_base; + struct pwm_chip chip; +}; + +#define to_pwm_imx1_chip(chip) container_of(chip, struct pwm_imx1_chip, chip) + +static int pwm_imx1_clk_prepare_enable(struct pwm_chip *chip) +{ + struct pwm_imx1_chip *imx = to_pwm_imx1_chip(chip); + int ret; + + ret = clk_prepare_enable(imx->clk_ipg); + if (ret) + return ret; + + ret = clk_prepare_enable(imx->clk_per); + if (ret) { + clk_disable_unprepare(imx->clk_ipg); + return ret; + } + + return 0; +} + +static void pwm_imx1_clk_disable_unprepare(struct pwm_chip *chip) +{ + struct pwm_imx1_chip *imx = to_pwm_imx1_chip(chip); + + clk_disable_unprepare(imx->clk_per); + clk_disable_unprepare(imx->clk_ipg); +} + +static int pwm_imx1_config(struct pwm_chip *chip, + struct pwm_device *pwm, int duty_ns, int period_ns) +{ + struct pwm_imx1_chip *imx = to_pwm_imx1_chip(chip); + u32 max, p; + + /* + * The PWM subsystem allows for exact frequencies. However, + * I cannot connect a scope on my device to the PWM line and + * thus cannot provide the program the PWM controller + * exactly. Instead, I'm relying on the fact that the + * Bootloader (u-boot or WinCE+haret) has programmed the PWM + * function group already. So I'll just modify the PWM sample + * register to follow the ratio of duty_ns vs. period_ns + * accordingly. + * + * This is good enough for programming the brightness of + * the LCD backlight. + * + * The real implementation would divide PERCLK[0] first by + * both the prescaler (/1 .. /128) and then by CLKSEL + * (/2 .. /16). + */ + max = readl(imx->mmio_base + MX1_PWMP); + p = max * duty_ns / period_ns; + + writel(max - p, imx->mmio_base + MX1_PWMS); + + return 0; +} + +static int pwm_imx1_enable(struct pwm_chip *chip, struct pwm_device *pwm) +{ + struct pwm_imx1_chip *imx = to_pwm_imx1_chip(chip); + u32 value; + int ret; + + ret = pwm_imx1_clk_prepare_enable(chip); + if (ret < 0) + return ret; + + value = readl(imx->mmio_base + MX1_PWMC); + value |= MX1_PWMC_EN; + writel(value, imx->mmio_base + MX1_PWMC); + + return 0; +} + +static void pwm_imx1_disable(struct pwm_chip *chip, struct pwm_device *pwm) +{ + struct pwm_imx1_chip *imx = to_pwm_imx1_chip(chip); + u32 value; + + value = readl(imx->mmio_base + MX1_PWMC); + value &= ~MX1_PWMC_EN; + writel(value, imx->mmio_base + MX1_PWMC); + + pwm_imx1_clk_disable_unprepare(chip); +} + +static const struct pwm_ops pwm_imx1_ops = { + .enable = pwm_imx1_enable, + .disable = pwm_imx1_disable, + .config = pwm_imx1_config, + .owner = THIS_MODULE, +}; + +static const struct of_device_id pwm_imx1_dt_ids[] = { + { .compatible = "fsl,imx1-pwm", }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, pwm_imx1_dt_ids); + +static int pwm_imx1_probe(struct platform_device *pdev) +{ + struct pwm_imx1_chip *imx; + struct resource *r; + + imx = devm_kzalloc(&pdev->dev, sizeof(*imx), GFP_KERNEL); + if (!imx) + return -ENOMEM; + + platform_set_drvdata(pdev, imx); + + imx->clk_ipg = devm_clk_get(&pdev->dev, "ipg"); + 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->clk_per = devm_clk_get(&pdev->dev, "per"); + if (IS_ERR(imx->clk_per)) { + int ret = PTR_ERR(imx->clk_per); + + if (ret != -EPROBE_DEFER) + dev_err(&pdev->dev, + "failed to get peripheral clock: %d\n", + ret); + + return ret; + } + + imx->chip.ops = &pwm_imx1_ops; + imx->chip.dev = &pdev->dev; + imx->chip.base = -1; + imx->chip.npwm = 1; + + r = platform_get_resource(pdev, IORESOURCE_MEM, 0); + imx->mmio_base = devm_ioremap_resource(&pdev->dev, r); + if (IS_ERR(imx->mmio_base)) + return PTR_ERR(imx->mmio_base); + + return pwmchip_add(&imx->chip); +} + +static int pwm_imx1_remove(struct platform_device *pdev) +{ + struct pwm_imx1_chip *imx = platform_get_drvdata(pdev); + + pwm_imx1_clk_disable_unprepare(&imx->chip); + + return pwmchip_remove(&imx->chip); +} + +static struct platform_driver pwm_imx1_driver = { + .driver = { + .name = "pwm-imx1", + .of_match_table = pwm_imx1_dt_ids, + }, + .probe = pwm_imx1_probe, + .remove = pwm_imx1_remove, +}; +module_platform_driver(pwm_imx1_driver); + +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Sascha Hauer <s.hauer@pengutronix.de>"); diff --git a/drivers/pwm/pwm-imx.c b/drivers/pwm/pwm-imx27.c index 55a3a363d5be..806130654211 100644 --- a/drivers/pwm/pwm-imx.c +++ b/drivers/pwm/pwm-imx27.c @@ -19,16 +19,6 @@ #include <linux/pwm.h> #include <linux/slab.h> -/* 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_EN BIT(4) - -/* i.MX27, i.MX31, i.MX35 share the same PWM function block: */ - #define MX3_PWMCR 0x00 /* PWM Control Register */ #define MX3_PWMSR 0x04 /* PWM Status Register */ #define MX3_PWMSAR 0x0C /* PWM Sample Register */ @@ -86,21 +76,18 @@ /* PWMPR register value of 0xffff has the same effect as 0xfffe */ #define MX3_PWMPR_MAX 0xfffe -struct imx_chip { +struct pwm_imx27_chip { struct clk *clk_ipg; - struct clk *clk_per; - void __iomem *mmio_base; - struct pwm_chip chip; }; -#define to_imx_chip(chip) container_of(chip, struct imx_chip, chip) +#define to_pwm_imx27_chip(chip) container_of(chip, struct pwm_imx27_chip, chip) -static int imx_pwm_clk_prepare_enable(struct pwm_chip *chip) +static int pwm_imx27_clk_prepare_enable(struct pwm_chip *chip) { - struct imx_chip *imx = to_imx_chip(chip); + struct pwm_imx27_chip *imx = to_pwm_imx27_chip(chip); int ret; ret = clk_prepare_enable(imx->clk_ipg); @@ -116,35 +103,32 @@ static int imx_pwm_clk_prepare_enable(struct pwm_chip *chip) return 0; } -static void imx_pwm_clk_disable_unprepare(struct pwm_chip *chip) +static void pwm_imx27_clk_disable_unprepare(struct pwm_chip *chip) { - struct imx_chip *imx = to_imx_chip(chip); + struct pwm_imx27_chip *imx = to_pwm_imx27_chip(chip); clk_disable_unprepare(imx->clk_per); clk_disable_unprepare(imx->clk_ipg); } -static void imx_pwm_get_state(struct pwm_chip *chip, - struct pwm_device *pwm, struct pwm_state *state) +static void pwm_imx27_get_state(struct pwm_chip *chip, + struct pwm_device *pwm, struct pwm_state *state) { - struct imx_chip *imx = to_imx_chip(chip); - u32 period, prescaler, pwm_clk, ret, val; + struct pwm_imx27_chip *imx = to_pwm_imx27_chip(chip); + u32 period, prescaler, pwm_clk, val; u64 tmp; + int ret; - ret = imx_pwm_clk_prepare_enable(chip); + ret = pwm_imx27_clk_prepare_enable(chip); if (ret < 0) return; val = readl(imx->mmio_base + MX3_PWMCR); - if (val & MX3_PWMCR_EN) { + if (val & MX3_PWMCR_EN) state->enabled = true; - ret = imx_pwm_clk_prepare_enable(chip); - if (ret) - return; - } else { + else state->enabled = false; - } switch (FIELD_GET(MX3_PWMCR_POUTC, val)) { case MX3_PWMCR_POUTC_NORMAL: @@ -176,70 +160,13 @@ static void imx_pwm_get_state(struct pwm_chip *chip, state->duty_cycle = 0; } - imx_pwm_clk_disable_unprepare(chip); -} - -static int imx_pwm_config_v1(struct pwm_chip *chip, - struct pwm_device *pwm, int duty_ns, int period_ns) -{ - struct imx_chip *imx = to_imx_chip(chip); - - /* - * The PWM subsystem allows for exact frequencies. However, - * I cannot connect a scope on my device to the PWM line and - * thus cannot provide the program the PWM controller - * exactly. Instead, I'm relying on the fact that the - * Bootloader (u-boot or WinCE+haret) has programmed the PWM - * function group already. So I'll just modify the PWM sample - * register to follow the ratio of duty_ns vs. period_ns - * accordingly. - * - * This is good enough for programming the brightness of - * the LCD backlight. - * - * The real implementation would divide PERCLK[0] first by - * both the prescaler (/1 .. /128) and then by CLKSEL - * (/2 .. /16). - */ - u32 max = readl(imx->mmio_base + MX1_PWMP); - u32 p = max * duty_ns / period_ns; - writel(max - p, imx->mmio_base + MX1_PWMS); - - return 0; -} - -static int imx_pwm_enable_v1(struct pwm_chip *chip, struct pwm_device *pwm) -{ - struct imx_chip *imx = to_imx_chip(chip); - u32 val; - int ret; - - ret = imx_pwm_clk_prepare_enable(chip); - if (ret < 0) - return ret; - - val = readl(imx->mmio_base + MX1_PWMC); - val |= MX1_PWMC_EN; - writel(val, imx->mmio_base + MX1_PWMC); - - return 0; -} - -static void imx_pwm_disable_v1(struct pwm_chip *chip, struct pwm_device *pwm) -{ - struct imx_chip *imx = to_imx_chip(chip); - u32 val; - - val = readl(imx->mmio_base + MX1_PWMC); - val &= ~MX1_PWMC_EN; - writel(val, imx->mmio_base + MX1_PWMC); - - imx_pwm_clk_disable_unprepare(chip); + if (!state->enabled) + pwm_imx27_clk_disable_unprepare(chip); } -static void imx_pwm_sw_reset(struct pwm_chip *chip) +static void pwm_imx27_sw_reset(struct pwm_chip *chip) { - struct imx_chip *imx = to_imx_chip(chip); + struct pwm_imx27_chip *imx = to_pwm_imx27_chip(chip); struct device *dev = chip->dev; int wait_count = 0; u32 cr; @@ -255,10 +182,10 @@ static void imx_pwm_sw_reset(struct pwm_chip *chip) dev_warn(dev, "software reset timeout\n"); } -static void imx_pwm_wait_fifo_slot(struct pwm_chip *chip, - struct pwm_device *pwm) +static void pwm_imx27_wait_fifo_slot(struct pwm_chip *chip, + struct pwm_device *pwm) { - struct imx_chip *imx = to_imx_chip(chip); + struct pwm_imx27_chip *imx = to_pwm_imx27_chip(chip); struct device *dev = chip->dev; unsigned int period_ms; int fifoav; @@ -277,11 +204,11 @@ static void imx_pwm_wait_fifo_slot(struct pwm_chip *chip, } } -static int imx_pwm_apply_v2(struct pwm_chip *chip, struct pwm_device *pwm, - struct pwm_state *state) +static int pwm_imx27_apply(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 pwm_imx27_chip *imx = to_pwm_imx27_chip(chip); struct pwm_state cstate; unsigned long long c; int ret; @@ -318,13 +245,13 @@ static int imx_pwm_apply_v2(struct pwm_chip *chip, struct pwm_device *pwm, * enabled. */ if (cstate.enabled) { - imx_pwm_wait_fifo_slot(chip, pwm); + pwm_imx27_wait_fifo_slot(chip, pwm); } else { - ret = imx_pwm_clk_prepare_enable(chip); + ret = pwm_imx27_clk_prepare_enable(chip); if (ret) return ret; - imx_pwm_sw_reset(chip); + pwm_imx27_sw_reset(chip); } writel(duty_cycles, imx->mmio_base + MX3_PWMSAR); @@ -343,64 +270,35 @@ static int imx_pwm_apply_v2(struct pwm_chip *chip, struct pwm_device *pwm, } else if (cstate.enabled) { writel(0, imx->mmio_base + MX3_PWMCR); - imx_pwm_clk_disable_unprepare(chip); + pwm_imx27_clk_disable_unprepare(chip); } return 0; } -static const struct pwm_ops imx_pwm_ops_v1 = { - .enable = imx_pwm_enable_v1, - .disable = imx_pwm_disable_v1, - .config = imx_pwm_config_v1, +static const struct pwm_ops pwm_imx27_ops = { + .apply = pwm_imx27_apply, + .get_state = pwm_imx27_get_state, .owner = THIS_MODULE, }; -static const struct pwm_ops imx_pwm_ops_v2 = { - .apply = imx_pwm_apply_v2, - .get_state = imx_pwm_get_state, - .owner = THIS_MODULE, -}; - -struct imx_pwm_data { - bool polarity_supported; - const struct pwm_ops *ops; -}; - -static struct imx_pwm_data imx_pwm_data_v1 = { - .ops = &imx_pwm_ops_v1, -}; - -static struct imx_pwm_data imx_pwm_data_v2 = { - .polarity_supported = true, - .ops = &imx_pwm_ops_v2, -}; - -static const struct of_device_id imx_pwm_dt_ids[] = { - { .compatible = "fsl,imx1-pwm", .data = &imx_pwm_data_v1, }, - { .compatible = "fsl,imx27-pwm", .data = &imx_pwm_data_v2, }, +static const struct of_device_id pwm_imx27_dt_ids[] = { + { .compatible = "fsl,imx27-pwm", }, { /* sentinel */ } }; -MODULE_DEVICE_TABLE(of, imx_pwm_dt_ids); +MODULE_DEVICE_TABLE(of, pwm_imx27_dt_ids); -static int imx_pwm_probe(struct platform_device *pdev) +static int pwm_imx27_probe(struct platform_device *pdev) { - const struct of_device_id *of_id = - of_match_device(imx_pwm_dt_ids, &pdev->dev); - const struct imx_pwm_data *data; - struct imx_chip *imx; + struct pwm_imx27_chip *imx; struct resource *r; - int ret = 0; - - if (!of_id) - return -ENODEV; - - data = of_id->data; imx = devm_kzalloc(&pdev->dev, sizeof(*imx), GFP_KERNEL); if (imx == NULL) return -ENOMEM; + platform_set_drvdata(pdev, imx); + imx->clk_ipg = devm_clk_get(&pdev->dev, "ipg"); if (IS_ERR(imx->clk_ipg)) { dev_err(&pdev->dev, "getting ipg clock failed with %ld\n", @@ -410,57 +308,51 @@ static int imx_pwm_probe(struct platform_device *pdev) imx->clk_per = devm_clk_get(&pdev->dev, "per"); if (IS_ERR(imx->clk_per)) { - dev_err(&pdev->dev, "getting per clock failed with %ld\n", - PTR_ERR(imx->clk_per)); - return PTR_ERR(imx->clk_per); + int ret = PTR_ERR(imx->clk_per); + + if (ret != -EPROBE_DEFER) + dev_err(&pdev->dev, + "failed to get peripheral clock: %d\n", + ret); + + return ret; } - imx->chip.ops = data->ops; + imx->chip.ops = &pwm_imx27_ops; imx->chip.dev = &pdev->dev; imx->chip.base = -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; - } + imx->chip.of_xlate = of_pwm_xlate_with_flags; + imx->chip.of_pwm_n_cells = 3; r = platform_get_resource(pdev, IORESOURCE_MEM, 0); imx->mmio_base = devm_ioremap_resource(&pdev->dev, r); if (IS_ERR(imx->mmio_base)) return PTR_ERR(imx->mmio_base); - ret = pwmchip_add(&imx->chip); - if (ret < 0) - return ret; - - platform_set_drvdata(pdev, imx); - return 0; + return pwmchip_add(&imx->chip); } -static int imx_pwm_remove(struct platform_device *pdev) +static int pwm_imx27_remove(struct platform_device *pdev) { - struct imx_chip *imx; + struct pwm_imx27_chip *imx; imx = platform_get_drvdata(pdev); - if (imx == NULL) - return -ENODEV; - imx_pwm_clk_disable_unprepare(&imx->chip); + pwm_imx27_clk_disable_unprepare(&imx->chip); return pwmchip_remove(&imx->chip); } static struct platform_driver imx_pwm_driver = { - .driver = { - .name = "imx-pwm", - .of_match_table = imx_pwm_dt_ids, + .driver = { + .name = "pwm-imx27", + .of_match_table = pwm_imx27_dt_ids, }, - .probe = imx_pwm_probe, - .remove = imx_pwm_remove, + .probe = pwm_imx27_probe, + .remove = pwm_imx27_remove, }; - module_platform_driver(imx_pwm_driver); MODULE_LICENSE("GPL v2"); diff --git a/drivers/pwm/pwm-mtk-disp.c b/drivers/pwm/pwm-mtk-disp.c index 893940d45f0d..15803c71fe80 100644 --- a/drivers/pwm/pwm-mtk-disp.c +++ b/drivers/pwm/pwm-mtk-disp.c @@ -277,10 +277,21 @@ static const struct mtk_pwm_data mt8173_pwm_data = { .commit_mask = 0x1, }; +static const struct mtk_pwm_data mt8183_pwm_data = { + .enable_mask = BIT(0), + .con0 = 0x18, + .con0_sel = 0x0, + .con1 = 0x1c, + .has_commit = false, + .bls_debug = 0x80, + .bls_debug_mask = 0x3, +}; + static const struct of_device_id mtk_disp_pwm_of_match[] = { { .compatible = "mediatek,mt2701-disp-pwm", .data = &mt2701_pwm_data}, { .compatible = "mediatek,mt6595-disp-pwm", .data = &mt8173_pwm_data}, { .compatible = "mediatek,mt8173-disp-pwm", .data = &mt8173_pwm_data}, + { .compatible = "mediatek,mt8183-disp-pwm", .data = &mt8183_pwm_data}, { } }; MODULE_DEVICE_TABLE(of, mtk_disp_pwm_of_match); diff --git a/drivers/pwm/pwm-rcar.c b/drivers/pwm/pwm-rcar.c index a41812fc6f95..cfe7dd1b448e 100644 --- a/drivers/pwm/pwm-rcar.c +++ b/drivers/pwm/pwm-rcar.c @@ -8,6 +8,8 @@ #include <linux/clk.h> #include <linux/err.h> #include <linux/io.h> +#include <linux/log2.h> +#include <linux/math64.h> #include <linux/module.h> #include <linux/of.h> #include <linux/platform_device.h> @@ -68,19 +70,15 @@ static void rcar_pwm_update(struct rcar_pwm_chip *rp, u32 mask, u32 data, static int rcar_pwm_get_clock_division(struct rcar_pwm_chip *rp, int period_ns) { unsigned long clk_rate = clk_get_rate(rp->clk); - unsigned long long max; /* max cycle / nanoseconds */ - unsigned int div; + u64 div, tmp; if (clk_rate == 0) return -EINVAL; - for (div = 0; div <= RCAR_PWM_MAX_DIVISION; div++) { - max = (unsigned long long)NSEC_PER_SEC * RCAR_PWM_MAX_CYCLE * - (1 << div); - do_div(max, clk_rate); - if (period_ns <= max) - break; - } + div = (u64)NSEC_PER_SEC * RCAR_PWM_MAX_CYCLE; + tmp = (u64)period_ns * clk_rate + div - 1; + tmp = div64_u64(tmp, div); + div = ilog2(tmp - 1) + 1; return (div <= RCAR_PWM_MAX_DIVISION) ? div : -ERANGE; } @@ -139,39 +137,8 @@ static void rcar_pwm_free(struct pwm_chip *chip, struct pwm_device *pwm) pm_runtime_put(chip->dev); } -static int rcar_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, - int duty_ns, int period_ns) +static int rcar_pwm_enable(struct rcar_pwm_chip *rp) { - struct rcar_pwm_chip *rp = to_rcar_pwm_chip(chip); - int div, ret; - - div = rcar_pwm_get_clock_division(rp, period_ns); - if (div < 0) - return div; - - /* - * Let the core driver set pwm->period if disabled and duty_ns == 0. - * But, this driver should prevent to set the new duty_ns if current - * duty_cycle is not set - */ - if (!pwm_is_enabled(pwm) && !duty_ns && !pwm->state.duty_cycle) - return 0; - - rcar_pwm_update(rp, RCAR_PWMCR_SYNC, RCAR_PWMCR_SYNC, RCAR_PWMCR); - - ret = rcar_pwm_set_counter(rp, div, duty_ns, period_ns); - if (!ret) - rcar_pwm_set_clock_control(rp, div); - - /* The SYNC should be set to 0 even if rcar_pwm_set_counter failed */ - rcar_pwm_update(rp, RCAR_PWMCR_SYNC, 0, RCAR_PWMCR); - - return ret; -} - -static int rcar_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm) -{ - struct rcar_pwm_chip *rp = to_rcar_pwm_chip(chip); u32 value; /* Don't enable the PWM device if CYC0 or PH0 is 0 */ @@ -185,19 +152,51 @@ static int rcar_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm) return 0; } -static void rcar_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm) +static void rcar_pwm_disable(struct rcar_pwm_chip *rp) +{ + rcar_pwm_update(rp, RCAR_PWMCR_EN0, 0, RCAR_PWMCR); +} + +static int rcar_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm, + struct pwm_state *state) { struct rcar_pwm_chip *rp = to_rcar_pwm_chip(chip); + struct pwm_state cur_state; + int div, ret; - rcar_pwm_update(rp, RCAR_PWMCR_EN0, 0, RCAR_PWMCR); + /* This HW/driver only supports normal polarity */ + pwm_get_state(pwm, &cur_state); + if (state->polarity != PWM_POLARITY_NORMAL) + return -ENOTSUPP; + + if (!state->enabled) { + rcar_pwm_disable(rp); + return 0; + } + + div = rcar_pwm_get_clock_division(rp, state->period); + if (div < 0) + return div; + + rcar_pwm_update(rp, RCAR_PWMCR_SYNC, RCAR_PWMCR_SYNC, RCAR_PWMCR); + + ret = rcar_pwm_set_counter(rp, div, state->duty_cycle, state->period); + if (!ret) + rcar_pwm_set_clock_control(rp, div); + + /* The SYNC should be set to 0 even if rcar_pwm_set_counter failed */ + rcar_pwm_update(rp, RCAR_PWMCR_SYNC, 0, RCAR_PWMCR); + + if (!ret && state->enabled) + ret = rcar_pwm_enable(rp); + + return ret; } static const struct pwm_ops rcar_pwm_ops = { .request = rcar_pwm_request, .free = rcar_pwm_free, - .config = rcar_pwm_config, - .enable = rcar_pwm_enable, - .disable = rcar_pwm_disable, + .apply = rcar_pwm_apply, .owner = THIS_MODULE, }; @@ -279,18 +278,16 @@ static int rcar_pwm_suspend(struct device *dev) static int rcar_pwm_resume(struct device *dev) { struct pwm_device *pwm = rcar_pwm_dev_to_pwm_dev(dev); + struct pwm_state state; if (!test_bit(PWMF_REQUESTED, &pwm->flags)) return 0; pm_runtime_get_sync(dev); - rcar_pwm_config(pwm->chip, pwm, pwm->state.duty_cycle, - pwm->state.period); - if (pwm_is_enabled(pwm)) - rcar_pwm_enable(pwm->chip, pwm); + pwm_get_state(pwm, &state); - return 0; + return rcar_pwm_apply(pwm->chip, pwm, &state); } #endif /* CONFIG_PM_SLEEP */ static SIMPLE_DEV_PM_OPS(rcar_pwm_pm_ops, rcar_pwm_suspend, rcar_pwm_resume); |