From 4dce82c1e84007d38747533673c2289bfc6497d2 Mon Sep 17 00:00:00 2001 From: Shawn Guo Date: Wed, 4 Apr 2012 10:50:52 +0800 Subject: pwm: add pwm-mxs support Add generic PWM framework driver (DT only) for Freescale MXS. Signed-off-by: Sascha Hauer Signed-off-by: Shawn Guo Signed-off-by: Thierry Reding --- drivers/pwm/pwm-mxs.c | 207 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 207 insertions(+) create mode 100644 drivers/pwm/pwm-mxs.c (limited to 'drivers/pwm/pwm-mxs.c') diff --git a/drivers/pwm/pwm-mxs.c b/drivers/pwm/pwm-mxs.c new file mode 100644 index 00000000000..9602708372b --- /dev/null +++ b/drivers/pwm/pwm-mxs.c @@ -0,0 +1,207 @@ +/* + * Copyright 2012 Freescale Semiconductor, Inc. + * + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define SET 0x4 +#define CLR 0x8 +#define TOG 0xc + +#define PWM_CTRL 0x0 +#define PWM_ACTIVE0 0x10 +#define PWM_PERIOD0 0x20 +#define PERIOD_PERIOD(p) ((p) & 0xffff) +#define PERIOD_PERIOD_MAX 0x10000 +#define PERIOD_ACTIVE_HIGH (3 << 16) +#define PERIOD_INACTIVE_LOW (2 << 18) +#define PERIOD_CDIV(div) (((div) & 0x7) << 20) +#define PERIOD_CDIV_MAX 8 + +struct mxs_pwm_chip { + struct pwm_chip chip; + struct device *dev; + struct clk *clk; + void __iomem *base; +}; + +#define to_mxs_pwm_chip(_chip) container_of(_chip, struct mxs_pwm_chip, chip) + +static int mxs_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, + int duty_ns, int period_ns) +{ + struct mxs_pwm_chip *mxs = to_mxs_pwm_chip(chip); + int ret, div = 0; + unsigned int period_cycles, duty_cycles; + unsigned long rate; + unsigned long long c; + + rate = clk_get_rate(mxs->clk); + while (1) { + c = rate / (1 << div); + c = c * period_ns; + do_div(c, 1000000000); + if (c < PERIOD_PERIOD_MAX) + break; + div++; + if (div > PERIOD_CDIV_MAX) + return -EINVAL; + } + + period_cycles = c; + c *= duty_ns; + do_div(c, period_ns); + duty_cycles = c; + + /* + * If the PWM channel is disabled, make sure to turn on the clock + * before writing the register. Otherwise, keep it enabled. + */ + if (!test_bit(PWMF_ENABLED, &pwm->flags)) { + ret = clk_prepare_enable(mxs->clk); + if (ret) + return ret; + } + + writel(duty_cycles << 16, + mxs->base + PWM_ACTIVE0 + pwm->hwpwm * 0x20); + writel(PERIOD_PERIOD(period_cycles) | PERIOD_ACTIVE_HIGH | + PERIOD_INACTIVE_LOW | PERIOD_CDIV(div), + mxs->base + PWM_PERIOD0 + pwm->hwpwm * 0x20); + + /* + * If the PWM is not enabled, turn the clock off again to save power. + */ + if (!test_bit(PWMF_ENABLED, &pwm->flags)) + clk_disable_unprepare(mxs->clk); + + return 0; +} + +static int mxs_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm) +{ + struct mxs_pwm_chip *mxs = to_mxs_pwm_chip(chip); + int ret; + + ret = clk_prepare_enable(mxs->clk); + if (ret) + return ret; + + writel(1 << pwm->hwpwm, mxs->base + PWM_CTRL + SET); + + return 0; +} + +static void mxs_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm) +{ + struct mxs_pwm_chip *mxs = to_mxs_pwm_chip(chip); + + writel(1 << pwm->hwpwm, mxs->base + PWM_CTRL + CLR); + + clk_disable_unprepare(mxs->clk); +} + +static const struct pwm_ops mxs_pwm_ops = { + .config = mxs_pwm_config, + .enable = mxs_pwm_enable, + .disable = mxs_pwm_disable, + .owner = THIS_MODULE, +}; + +static int mxs_pwm_probe(struct platform_device *pdev) +{ + struct device_node *np = pdev->dev.of_node; + struct mxs_pwm_chip *mxs; + int ret; + + mxs = devm_kzalloc(&pdev->dev, sizeof(*mxs), GFP_KERNEL); + if (!mxs) + return -ENOMEM; + + mxs->base = of_iomap(np, 0); + if (!mxs->base) + return -EADDRNOTAVAIL; + + mxs->clk = clk_get(&pdev->dev, NULL); + if (IS_ERR(mxs->clk)) { + ret = PTR_ERR(mxs->clk); + goto iounmap; + } + + mxs->chip.dev = &pdev->dev; + mxs->chip.ops = &mxs_pwm_ops; + mxs->chip.base = -1; + ret = of_property_read_u32(np, "fsl,pwm-number", &mxs->chip.npwm); + if (ret < 0) { + dev_err(&pdev->dev, "failed to get pwm number: %d\n", ret); + goto clk_put; + } + + ret = pwmchip_add(&mxs->chip); + if (ret < 0) { + dev_err(&pdev->dev, "failed to add pwm chip %d\n", ret); + goto clk_put; + } + + mxs->dev = &pdev->dev; + platform_set_drvdata(pdev, mxs); + + mxs_reset_block(mxs->base); + + return 0; + +clk_put: + clk_put(mxs->clk); +iounmap: + iounmap(mxs->base); + return ret; +} + +static int __devexit mxs_pwm_remove(struct platform_device *pdev) +{ + struct mxs_pwm_chip *mxs = platform_get_drvdata(pdev); + + pwmchip_remove(&mxs->chip); + clk_put(mxs->clk); + iounmap(mxs->base); + + return 0; +} + +static struct of_device_id mxs_pwm_dt_ids[] = { + { .compatible = "fsl,mxs-pwm", }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, mxs_pwm_dt_ids); + +static struct platform_driver mxs_pwm_driver = { + .driver = { + .name = "mxs-pwm", + .of_match_table = of_match_ptr(mxs_pwm_dt_ids), + }, + .probe = mxs_pwm_probe, + .remove = __devexit_p(mxs_pwm_remove), +}; +module_platform_driver(mxs_pwm_driver); + +MODULE_ALIAS("platform:mxs-pwm"); +MODULE_AUTHOR("Shawn Guo "); +MODULE_DESCRIPTION("Freescale MXS PWM Driver"); +MODULE_LICENSE("GPL v2"); -- cgit v1.2.3 From 071407eefd708d7e273b67af150f8dff6c51eb58 Mon Sep 17 00:00:00 2001 From: Shawn Guo Date: Tue, 26 Jun 2012 16:58:08 +0800 Subject: pwm: pwm-mxs: encode soc name in compatible string Encode soc name in the compatible string to know the specific version hardware block. This is the general approach adopted for most bindings. Change mxs-pwm binding to use the approach. Signed-off-by: Shawn Guo Signed-off-by: Thierry Reding --- drivers/pwm/pwm-mxs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/pwm/pwm-mxs.c') diff --git a/drivers/pwm/pwm-mxs.c b/drivers/pwm/pwm-mxs.c index 9602708372b..d9a38050a50 100644 --- a/drivers/pwm/pwm-mxs.c +++ b/drivers/pwm/pwm-mxs.c @@ -186,7 +186,7 @@ static int __devexit mxs_pwm_remove(struct platform_device *pdev) } static struct of_device_id mxs_pwm_dt_ids[] = { - { .compatible = "fsl,mxs-pwm", }, + { .compatible = "fsl,imx23-pwm", }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, mxs_pwm_dt_ids); -- cgit v1.2.3 From 01bf32e94968fd13804062859cfd935efebc11e9 Mon Sep 17 00:00:00 2001 From: Shawn Guo Date: Tue, 26 Jun 2012 16:58:09 +0800 Subject: pwm: pwm-mxs: use global reset function stmp_reset_block Use global reset function stmp_reset_block instead of mxs_reset_block to remove inclusion. Signed-off-by: Shawn Guo Signed-off-by: Thierry Reding --- drivers/pwm/pwm-mxs.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/pwm/pwm-mxs.c') diff --git a/drivers/pwm/pwm-mxs.c b/drivers/pwm/pwm-mxs.c index d9a38050a50..23e524bbee0 100644 --- a/drivers/pwm/pwm-mxs.c +++ b/drivers/pwm/pwm-mxs.c @@ -19,7 +19,7 @@ #include #include #include -#include +#include #define SET 0x4 #define CLR 0x8 @@ -163,7 +163,7 @@ static int mxs_pwm_probe(struct platform_device *pdev) mxs->dev = &pdev->dev; platform_set_drvdata(pdev, mxs); - mxs_reset_block(mxs->base); + stmp_reset_block(mxs->base); return 0; -- cgit v1.2.3 From 22d260bd88452ae81c80514810434d4cc98b7978 Mon Sep 17 00:00:00 2001 From: Shawn Guo Date: Tue, 26 Jun 2012 16:58:10 +0800 Subject: pwm: pwm-mxs: use devm_* managed functions Use devm_* managed functions to have a clean fail-out. Signed-off-by: Shawn Guo Signed-off-by: Thierry Reding --- drivers/pwm/pwm-mxs.c | 24 ++++++++---------------- 1 file changed, 8 insertions(+), 16 deletions(-) (limited to 'drivers/pwm/pwm-mxs.c') diff --git a/drivers/pwm/pwm-mxs.c b/drivers/pwm/pwm-mxs.c index 23e524bbee0..3b7f3ab116e 100644 --- a/drivers/pwm/pwm-mxs.c +++ b/drivers/pwm/pwm-mxs.c @@ -129,21 +129,21 @@ static int mxs_pwm_probe(struct platform_device *pdev) { struct device_node *np = pdev->dev.of_node; struct mxs_pwm_chip *mxs; + struct resource *res; int ret; mxs = devm_kzalloc(&pdev->dev, sizeof(*mxs), GFP_KERNEL); if (!mxs) return -ENOMEM; - mxs->base = of_iomap(np, 0); + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + mxs->base = devm_request_and_ioremap(&pdev->dev, res); if (!mxs->base) return -EADDRNOTAVAIL; - mxs->clk = clk_get(&pdev->dev, NULL); - if (IS_ERR(mxs->clk)) { - ret = PTR_ERR(mxs->clk); - goto iounmap; - } + mxs->clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(mxs->clk)) + return PTR_ERR(mxs->clk); mxs->chip.dev = &pdev->dev; mxs->chip.ops = &mxs_pwm_ops; @@ -151,13 +151,13 @@ static int mxs_pwm_probe(struct platform_device *pdev) ret = of_property_read_u32(np, "fsl,pwm-number", &mxs->chip.npwm); if (ret < 0) { dev_err(&pdev->dev, "failed to get pwm number: %d\n", ret); - goto clk_put; + return ret; } ret = pwmchip_add(&mxs->chip); if (ret < 0) { dev_err(&pdev->dev, "failed to add pwm chip %d\n", ret); - goto clk_put; + return ret; } mxs->dev = &pdev->dev; @@ -166,12 +166,6 @@ static int mxs_pwm_probe(struct platform_device *pdev) stmp_reset_block(mxs->base); return 0; - -clk_put: - clk_put(mxs->clk); -iounmap: - iounmap(mxs->base); - return ret; } static int __devexit mxs_pwm_remove(struct platform_device *pdev) @@ -179,8 +173,6 @@ static int __devexit mxs_pwm_remove(struct platform_device *pdev) struct mxs_pwm_chip *mxs = platform_get_drvdata(pdev); pwmchip_remove(&mxs->chip); - clk_put(mxs->clk); - iounmap(mxs->base); return 0; } -- cgit v1.2.3 From 1112fe88d4ebbb5f46750726b87fb0beb1396835 Mon Sep 17 00:00:00 2001 From: Shawn Guo Date: Tue, 26 Jun 2012 16:58:11 +0800 Subject: pwm: pwm-mxs: add pinctrl support Call pinctrl subsystem to set up pwm pin. Signed-off-by: Shawn Guo Signed-off-by: Thierry Reding --- drivers/pwm/pwm-mxs.c | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'drivers/pwm/pwm-mxs.c') diff --git a/drivers/pwm/pwm-mxs.c b/drivers/pwm/pwm-mxs.c index 3b7f3ab116e..269d7c2a87d 100644 --- a/drivers/pwm/pwm-mxs.c +++ b/drivers/pwm/pwm-mxs.c @@ -16,6 +16,7 @@ #include #include #include +#include #include #include #include @@ -130,6 +131,7 @@ static int mxs_pwm_probe(struct platform_device *pdev) struct device_node *np = pdev->dev.of_node; struct mxs_pwm_chip *mxs; struct resource *res; + struct pinctrl *pinctrl; int ret; mxs = devm_kzalloc(&pdev->dev, sizeof(*mxs), GFP_KERNEL); @@ -141,6 +143,10 @@ static int mxs_pwm_probe(struct platform_device *pdev) if (!mxs->base) return -EADDRNOTAVAIL; + pinctrl = devm_pinctrl_get_select_default(&pdev->dev); + if (IS_ERR(pinctrl)) + return PTR_ERR(pinctrl); + mxs->clk = devm_clk_get(&pdev->dev, NULL); if (IS_ERR(mxs->clk)) return PTR_ERR(mxs->clk); -- cgit v1.2.3 From 457fd768ee1f7c78e85dfbe86329e6b24eb36514 Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Sun, 1 Jul 2012 12:58:00 +0800 Subject: pwm: pwm-mxs: Return proper error if pwmchip_remove() fails Signed-off-by: Axel Lin Acked-by: Shawn Guo Signed-off-by: Thierry Reding --- drivers/pwm/pwm-mxs.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'drivers/pwm/pwm-mxs.c') diff --git a/drivers/pwm/pwm-mxs.c b/drivers/pwm/pwm-mxs.c index 269d7c2a87d..e5852646f08 100644 --- a/drivers/pwm/pwm-mxs.c +++ b/drivers/pwm/pwm-mxs.c @@ -178,9 +178,7 @@ static int __devexit mxs_pwm_remove(struct platform_device *pdev) { struct mxs_pwm_chip *mxs = platform_get_drvdata(pdev); - pwmchip_remove(&mxs->chip); - - return 0; + return pwmchip_remove(&mxs->chip); } static struct of_device_id mxs_pwm_dt_ids[] = { -- cgit v1.2.3