root/drivers/pwm/pwm-mxs.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. mxs_pwm_config
  2. mxs_pwm_enable
  3. mxs_pwm_disable
  4. mxs_pwm_probe
  5. mxs_pwm_remove

   1 // SPDX-License-Identifier: GPL-2.0+
   2 /*
   3  * Copyright 2012 Freescale Semiconductor, Inc.
   4  */
   5 
   6 #include <linux/clk.h>
   7 #include <linux/err.h>
   8 #include <linux/io.h>
   9 #include <linux/kernel.h>
  10 #include <linux/module.h>
  11 #include <linux/of.h>
  12 #include <linux/of_address.h>
  13 #include <linux/platform_device.h>
  14 #include <linux/pwm.h>
  15 #include <linux/slab.h>
  16 #include <linux/stmp_device.h>
  17 
  18 #define SET     0x4
  19 #define CLR     0x8
  20 #define TOG     0xc
  21 
  22 #define PWM_CTRL                0x0
  23 #define PWM_ACTIVE0             0x10
  24 #define PWM_PERIOD0             0x20
  25 #define  PERIOD_PERIOD(p)       ((p) & 0xffff)
  26 #define  PERIOD_PERIOD_MAX      0x10000
  27 #define  PERIOD_ACTIVE_HIGH     (3 << 16)
  28 #define  PERIOD_INACTIVE_LOW    (2 << 18)
  29 #define  PERIOD_CDIV(div)       (((div) & 0x7) << 20)
  30 #define  PERIOD_CDIV_MAX        8
  31 
  32 static const unsigned int cdiv[PERIOD_CDIV_MAX] = {
  33         1, 2, 4, 8, 16, 64, 256, 1024
  34 };
  35 
  36 struct mxs_pwm_chip {
  37         struct pwm_chip chip;
  38         struct clk *clk;
  39         void __iomem *base;
  40 };
  41 
  42 #define to_mxs_pwm_chip(_chip) container_of(_chip, struct mxs_pwm_chip, chip)
  43 
  44 static int mxs_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
  45                           int duty_ns, int period_ns)
  46 {
  47         struct mxs_pwm_chip *mxs = to_mxs_pwm_chip(chip);
  48         int ret, div = 0;
  49         unsigned int period_cycles, duty_cycles;
  50         unsigned long rate;
  51         unsigned long long c;
  52 
  53         rate = clk_get_rate(mxs->clk);
  54         while (1) {
  55                 c = rate / cdiv[div];
  56                 c = c * period_ns;
  57                 do_div(c, 1000000000);
  58                 if (c < PERIOD_PERIOD_MAX)
  59                         break;
  60                 div++;
  61                 if (div >= PERIOD_CDIV_MAX)
  62                         return -EINVAL;
  63         }
  64 
  65         period_cycles = c;
  66         c *= duty_ns;
  67         do_div(c, period_ns);
  68         duty_cycles = c;
  69 
  70         /*
  71          * If the PWM channel is disabled, make sure to turn on the clock
  72          * before writing the register. Otherwise, keep it enabled.
  73          */
  74         if (!pwm_is_enabled(pwm)) {
  75                 ret = clk_prepare_enable(mxs->clk);
  76                 if (ret)
  77                         return ret;
  78         }
  79 
  80         writel(duty_cycles << 16,
  81                         mxs->base + PWM_ACTIVE0 + pwm->hwpwm * 0x20);
  82         writel(PERIOD_PERIOD(period_cycles) | PERIOD_ACTIVE_HIGH |
  83                PERIOD_INACTIVE_LOW | PERIOD_CDIV(div),
  84                         mxs->base + PWM_PERIOD0 + pwm->hwpwm * 0x20);
  85 
  86         /*
  87          * If the PWM is not enabled, turn the clock off again to save power.
  88          */
  89         if (!pwm_is_enabled(pwm))
  90                 clk_disable_unprepare(mxs->clk);
  91 
  92         return 0;
  93 }
  94 
  95 static int mxs_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm)
  96 {
  97         struct mxs_pwm_chip *mxs = to_mxs_pwm_chip(chip);
  98         int ret;
  99 
 100         ret = clk_prepare_enable(mxs->clk);
 101         if (ret)
 102                 return ret;
 103 
 104         writel(1 << pwm->hwpwm, mxs->base + PWM_CTRL + SET);
 105 
 106         return 0;
 107 }
 108 
 109 static void mxs_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm)
 110 {
 111         struct mxs_pwm_chip *mxs = to_mxs_pwm_chip(chip);
 112 
 113         writel(1 << pwm->hwpwm, mxs->base + PWM_CTRL + CLR);
 114 
 115         clk_disable_unprepare(mxs->clk);
 116 }
 117 
 118 static const struct pwm_ops mxs_pwm_ops = {
 119         .config = mxs_pwm_config,
 120         .enable = mxs_pwm_enable,
 121         .disable = mxs_pwm_disable,
 122         .owner = THIS_MODULE,
 123 };
 124 
 125 static int mxs_pwm_probe(struct platform_device *pdev)
 126 {
 127         struct device_node *np = pdev->dev.of_node;
 128         struct mxs_pwm_chip *mxs;
 129         int ret;
 130 
 131         mxs = devm_kzalloc(&pdev->dev, sizeof(*mxs), GFP_KERNEL);
 132         if (!mxs)
 133                 return -ENOMEM;
 134 
 135         mxs->base = devm_platform_ioremap_resource(pdev, 0);
 136         if (IS_ERR(mxs->base))
 137                 return PTR_ERR(mxs->base);
 138 
 139         mxs->clk = devm_clk_get(&pdev->dev, NULL);
 140         if (IS_ERR(mxs->clk))
 141                 return PTR_ERR(mxs->clk);
 142 
 143         mxs->chip.dev = &pdev->dev;
 144         mxs->chip.ops = &mxs_pwm_ops;
 145         mxs->chip.base = -1;
 146 
 147         ret = of_property_read_u32(np, "fsl,pwm-number", &mxs->chip.npwm);
 148         if (ret < 0) {
 149                 dev_err(&pdev->dev, "failed to get pwm number: %d\n", ret);
 150                 return ret;
 151         }
 152 
 153         ret = pwmchip_add(&mxs->chip);
 154         if (ret < 0) {
 155                 dev_err(&pdev->dev, "failed to add pwm chip %d\n", ret);
 156                 return ret;
 157         }
 158 
 159         platform_set_drvdata(pdev, mxs);
 160 
 161         ret = stmp_reset_block(mxs->base);
 162         if (ret)
 163                 goto pwm_remove;
 164 
 165         return 0;
 166 
 167 pwm_remove:
 168         pwmchip_remove(&mxs->chip);
 169         return ret;
 170 }
 171 
 172 static int mxs_pwm_remove(struct platform_device *pdev)
 173 {
 174         struct mxs_pwm_chip *mxs = platform_get_drvdata(pdev);
 175 
 176         return pwmchip_remove(&mxs->chip);
 177 }
 178 
 179 static const struct of_device_id mxs_pwm_dt_ids[] = {
 180         { .compatible = "fsl,imx23-pwm", },
 181         { /* sentinel */ }
 182 };
 183 MODULE_DEVICE_TABLE(of, mxs_pwm_dt_ids);
 184 
 185 static struct platform_driver mxs_pwm_driver = {
 186         .driver = {
 187                 .name = "mxs-pwm",
 188                 .of_match_table = mxs_pwm_dt_ids,
 189         },
 190         .probe = mxs_pwm_probe,
 191         .remove = mxs_pwm_remove,
 192 };
 193 module_platform_driver(mxs_pwm_driver);
 194 
 195 MODULE_ALIAS("platform:mxs-pwm");
 196 MODULE_AUTHOR("Shawn Guo <shawn.guo@linaro.org>");
 197 MODULE_DESCRIPTION("Freescale MXS PWM Driver");
 198 MODULE_LICENSE("GPL v2");

/* [<][>][^][v][top][bottom][index][help] */