From c009c56475a08fc8c0e1d4d4161bdcc76f3638b7 Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Mon, 11 Jul 2016 11:08:29 +0200 Subject: pwm: tegra: Drop NUM_PWM macro This macro is used to initialize the ->npwm field of the PWM chip. Use a literal instead and make all other places rely on ->npwm. Signed-off-by: Thierry Reding --- drivers/pwm/pwm-tegra.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) (limited to 'drivers/pwm/pwm-tegra.c') diff --git a/drivers/pwm/pwm-tegra.c b/drivers/pwm/pwm-tegra.c index d4de0607b502..d42b636c9695 100644 --- a/drivers/pwm/pwm-tegra.c +++ b/drivers/pwm/pwm-tegra.c @@ -36,8 +36,6 @@ #define PWM_SCALE_WIDTH 13 #define PWM_SCALE_SHIFT 0 -#define NUM_PWM 4 - struct tegra_pwm_chip { struct pwm_chip chip; struct device *dev; @@ -192,7 +190,7 @@ static int tegra_pwm_probe(struct platform_device *pdev) pwm->chip.dev = &pdev->dev; pwm->chip.ops = &tegra_pwm_ops; pwm->chip.base = -1; - pwm->chip.npwm = NUM_PWM; + pwm->chip.npwm = 4; ret = pwmchip_add(&pwm->chip); if (ret < 0) { @@ -206,12 +204,12 @@ static int tegra_pwm_probe(struct platform_device *pdev) static int tegra_pwm_remove(struct platform_device *pdev) { struct tegra_pwm_chip *pc = platform_get_drvdata(pdev); - int i; + unsigned int i; if (WARN_ON(!pc)) return -ENODEV; - for (i = 0; i < NUM_PWM; i++) { + for (i = 0; i < pc->chip.npwm; i++) { struct pwm_device *pwm = &pc->chip.pwms[i]; if (!pwm_is_enabled(pwm)) -- cgit v1.2.3 From e17c0b2258834b88bdaa282a30e125071d9325fc Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Mon, 11 Jul 2016 11:26:52 +0200 Subject: pwm: tegra: Remove useless padding Use single spaces to separate data type from field names in structure definitions. Signed-off-by: Thierry Reding --- drivers/pwm/pwm-tegra.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers/pwm/pwm-tegra.c') diff --git a/drivers/pwm/pwm-tegra.c b/drivers/pwm/pwm-tegra.c index d42b636c9695..ec5b79f08f17 100644 --- a/drivers/pwm/pwm-tegra.c +++ b/drivers/pwm/pwm-tegra.c @@ -37,12 +37,12 @@ #define PWM_SCALE_SHIFT 0 struct tegra_pwm_chip { - struct pwm_chip chip; - struct device *dev; + struct pwm_chip chip; + struct device *dev; - struct clk *clk; + struct clk *clk; - void __iomem *mmio_base; + void __iomem *mmio_base; }; static inline struct tegra_pwm_chip *to_tegra_pwm_chip(struct pwm_chip *chip) -- cgit v1.2.3 From 4f57f5a01fcb4ec9d98e274837c0cd853d447da3 Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Mon, 11 Jul 2016 11:27:29 +0200 Subject: pwm: tegra: Rename mmio_base to regs The former is much longer to type and is ambiguous because the value stored in the field is not the (physical) base address of the memory- mapped I/O registers, but the virtual address of those registers as mapped through the MMU. Signed-off-by: Thierry Reding --- drivers/pwm/pwm-tegra.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'drivers/pwm/pwm-tegra.c') diff --git a/drivers/pwm/pwm-tegra.c b/drivers/pwm/pwm-tegra.c index ec5b79f08f17..3cbb32d8064f 100644 --- a/drivers/pwm/pwm-tegra.c +++ b/drivers/pwm/pwm-tegra.c @@ -42,7 +42,7 @@ struct tegra_pwm_chip { struct clk *clk; - void __iomem *mmio_base; + void __iomem *regs; }; static inline struct tegra_pwm_chip *to_tegra_pwm_chip(struct pwm_chip *chip) @@ -52,13 +52,13 @@ static inline struct tegra_pwm_chip *to_tegra_pwm_chip(struct pwm_chip *chip) static inline u32 pwm_readl(struct tegra_pwm_chip *chip, unsigned int num) { - return readl(chip->mmio_base + (num << 4)); + return readl(chip->regs + (num << 4)); } static inline void pwm_writel(struct tegra_pwm_chip *chip, unsigned int num, unsigned long val) { - writel(val, chip->mmio_base + (num << 4)); + writel(val, chip->regs + (num << 4)); } static int tegra_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, @@ -177,9 +177,9 @@ static int tegra_pwm_probe(struct platform_device *pdev) pwm->dev = &pdev->dev; r = platform_get_resource(pdev, IORESOURCE_MEM, 0); - pwm->mmio_base = devm_ioremap_resource(&pdev->dev, r); - if (IS_ERR(pwm->mmio_base)) - return PTR_ERR(pwm->mmio_base); + pwm->regs = devm_ioremap_resource(&pdev->dev, r); + if (IS_ERR(pwm->regs)) + return PTR_ERR(pwm->regs); platform_set_drvdata(pdev, pwm); -- cgit v1.2.3 From 5dfbd2bd5439f1ada5ddaa3883e9e038de5d2abe Mon Sep 17 00:00:00 2001 From: Rohith Seelaboyina Date: Wed, 22 Jun 2016 17:17:19 +0530 Subject: pwm: tegra: Add support for reset control Add reset control of the PWM controller to reset it before accessing the PWM register. Signed-off-by: Rohith Seelaboyina Signed-off-by: Laxman Dewangan Signed-off-by: Thierry Reding --- drivers/pwm/pwm-tegra.c | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) (limited to 'drivers/pwm/pwm-tegra.c') diff --git a/drivers/pwm/pwm-tegra.c b/drivers/pwm/pwm-tegra.c index 3cbb32d8064f..097658e0751b 100644 --- a/drivers/pwm/pwm-tegra.c +++ b/drivers/pwm/pwm-tegra.c @@ -29,6 +29,7 @@ #include #include #include +#include #define PWM_ENABLE (1 << 31) #define PWM_DUTY_WIDTH 8 @@ -41,6 +42,7 @@ struct tegra_pwm_chip { struct device *dev; struct clk *clk; + struct reset_control*rst; void __iomem *regs; }; @@ -187,6 +189,15 @@ static int tegra_pwm_probe(struct platform_device *pdev) if (IS_ERR(pwm->clk)) return PTR_ERR(pwm->clk); + pwm->rst = devm_reset_control_get(&pdev->dev, "pwm"); + if (IS_ERR(pwm->rst)) { + ret = PTR_ERR(pwm->rst); + dev_err(&pdev->dev, "Reset control is not found: %d\n", ret); + return ret; + } + + reset_control_deassert(pwm->rst); + pwm->chip.dev = &pdev->dev; pwm->chip.ops = &tegra_pwm_ops; pwm->chip.base = -1; @@ -195,6 +206,7 @@ static int tegra_pwm_probe(struct platform_device *pdev) ret = pwmchip_add(&pwm->chip); if (ret < 0) { dev_err(&pdev->dev, "pwmchip_add() failed: %d\n", ret); + reset_control_assert(pwm->rst); return ret; } @@ -205,10 +217,15 @@ static int tegra_pwm_remove(struct platform_device *pdev) { struct tegra_pwm_chip *pc = platform_get_drvdata(pdev); unsigned int i; + int err; if (WARN_ON(!pc)) return -ENODEV; + err = clk_prepare_enable(pc->clk); + if (err < 0) + return err; + for (i = 0; i < pc->chip.npwm; i++) { struct pwm_device *pwm = &pc->chip.pwms[i]; @@ -221,6 +238,9 @@ static int tegra_pwm_remove(struct platform_device *pdev) clk_disable_unprepare(pc->clk); } + reset_control_assert(pc->rst); + clk_disable_unprepare(pc->clk); + return pwmchip_remove(&pc->chip); } -- cgit v1.2.3 From e0ee1a75f412cc9f3b16127742a30baf975ec51f Mon Sep 17 00:00:00 2001 From: "Victor(Weiguo) Pan" Date: Wed, 22 Jun 2016 17:17:20 +0530 Subject: pwm: tegra: Allow 100 % duty cycle To get 100 % duty cycle (always high), pulse width needs to be set to 256. Signed-off-by: Victor(Weiguo) Pan Signed-off-by: Laxman Dewangan Signed-off-by: Thierry Reding --- drivers/pwm/pwm-tegra.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/pwm/pwm-tegra.c') diff --git a/drivers/pwm/pwm-tegra.c b/drivers/pwm/pwm-tegra.c index 097658e0751b..2026eaa932ae 100644 --- a/drivers/pwm/pwm-tegra.c +++ b/drivers/pwm/pwm-tegra.c @@ -77,7 +77,7 @@ static int tegra_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, * per (1 << PWM_DUTY_WIDTH) cycles and make sure to round to the * nearest integer during division. */ - c = duty_ns * ((1 << PWM_DUTY_WIDTH) - 1) + period_ns / 2; + c = duty_ns * (1 << PWM_DUTY_WIDTH) + period_ns / 2; do_div(c, period_ns); val = (u32)c << PWM_DUTY_SHIFT; -- cgit v1.2.3 From b979ed531468e9848d95a3b788dd8490927417e3 Mon Sep 17 00:00:00 2001 From: Hyong Bin Kim Date: Wed, 22 Jun 2016 17:17:21 +0530 Subject: pwm: tegra: Avoid overflow when calculating duty cycle duty_ns * (1 << PWM_DUTY_WIDTH) could overflow in integer calculation when the PWM rate is low. Hence do all calculation on unsigned long long to avoid overflow. Signed-off-by: Hyong Bin Kim Signed-off-by: Laxman Dewangan Signed-off-by: Thierry Reding --- drivers/pwm/pwm-tegra.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'drivers/pwm/pwm-tegra.c') diff --git a/drivers/pwm/pwm-tegra.c b/drivers/pwm/pwm-tegra.c index 2026eaa932ae..247156149b44 100644 --- a/drivers/pwm/pwm-tegra.c +++ b/drivers/pwm/pwm-tegra.c @@ -67,7 +67,7 @@ static int tegra_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, int duty_ns, int period_ns) { struct tegra_pwm_chip *pc = to_tegra_pwm_chip(chip); - unsigned long long c; + unsigned long long c = duty_ns; unsigned long rate, hz; u32 val = 0; int err; @@ -77,7 +77,8 @@ static int tegra_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, * per (1 << PWM_DUTY_WIDTH) cycles and make sure to round to the * nearest integer during division. */ - c = duty_ns * (1 << PWM_DUTY_WIDTH) + period_ns / 2; + c *= (1 << PWM_DUTY_WIDTH); + c += period_ns / 2; do_div(c, period_ns); val = (u32)c << PWM_DUTY_SHIFT; -- cgit v1.2.3 From e9be88a2f06b89a56499c9bfe494ddc4e5f55f46 Mon Sep 17 00:00:00 2001 From: Laxman Dewangan Date: Wed, 22 Jun 2016 17:17:23 +0530 Subject: pwm: tegra: Add support for Tegra186 Tegra186 has multiple PWM controllers with only one output instead of one controller with four outputs in earlier SoC generations. Add support for Tegra186 and detect the number of PWM outputs using device tree match data. Signed-off-by: Laxman Dewangan Reviewed-by: Alexandre Courbot Signed-off-by: Thierry Reding --- drivers/pwm/pwm-tegra.c | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) (limited to 'drivers/pwm/pwm-tegra.c') diff --git a/drivers/pwm/pwm-tegra.c b/drivers/pwm/pwm-tegra.c index 247156149b44..e4647840cd6e 100644 --- a/drivers/pwm/pwm-tegra.c +++ b/drivers/pwm/pwm-tegra.c @@ -26,6 +26,7 @@ #include #include #include +#include #include #include #include @@ -37,6 +38,10 @@ #define PWM_SCALE_WIDTH 13 #define PWM_SCALE_SHIFT 0 +struct tegra_pwm_soc { + unsigned int num_channels; +}; + struct tegra_pwm_chip { struct pwm_chip chip; struct device *dev; @@ -45,6 +50,8 @@ struct tegra_pwm_chip { struct reset_control*rst; void __iomem *regs; + + const struct tegra_pwm_soc *soc; }; static inline struct tegra_pwm_chip *to_tegra_pwm_chip(struct pwm_chip *chip) @@ -177,6 +184,7 @@ static int tegra_pwm_probe(struct platform_device *pdev) if (!pwm) return -ENOMEM; + pwm->soc = of_device_get_match_data(&pdev->dev); pwm->dev = &pdev->dev; r = platform_get_resource(pdev, IORESOURCE_MEM, 0); @@ -202,7 +210,7 @@ static int tegra_pwm_probe(struct platform_device *pdev) pwm->chip.dev = &pdev->dev; pwm->chip.ops = &tegra_pwm_ops; pwm->chip.base = -1; - pwm->chip.npwm = 4; + pwm->chip.npwm = pwm->soc->num_channels; ret = pwmchip_add(&pwm->chip); if (ret < 0) { @@ -245,9 +253,17 @@ static int tegra_pwm_remove(struct platform_device *pdev) return pwmchip_remove(&pc->chip); } +static const struct tegra_pwm_soc tegra20_pwm_soc = { + .num_channels = 4, +}; + +static const struct tegra_pwm_soc tegra186_pwm_soc = { + .num_channels = 1, +}; + static const struct of_device_id tegra_pwm_of_match[] = { - { .compatible = "nvidia,tegra20-pwm" }, - { .compatible = "nvidia,tegra30-pwm" }, + { .compatible = "nvidia,tegra20-pwm", .data = &tegra20_pwm_soc }, + { .compatible = "nvidia,tegra186-pwm", .data = &tegra186_pwm_soc }, { } }; -- cgit v1.2.3