Back to home page

OSCL-LXR

 
 

    


0001 /*
0002  * ST Microelectronics SPEAr Pulse Width Modulator driver
0003  *
0004  * Copyright (C) 2012 ST Microelectronics
0005  * Shiraz Hashim <shiraz.linux.kernel@gmail.com>
0006  *
0007  * This file is licensed under the terms of the GNU General Public
0008  * License version 2. This program is licensed "as is" without any
0009  * warranty of any kind, whether express or implied.
0010  */
0011 
0012 #include <linux/clk.h>
0013 #include <linux/err.h>
0014 #include <linux/io.h>
0015 #include <linux/ioport.h>
0016 #include <linux/kernel.h>
0017 #include <linux/math64.h>
0018 #include <linux/module.h>
0019 #include <linux/of.h>
0020 #include <linux/platform_device.h>
0021 #include <linux/pwm.h>
0022 #include <linux/slab.h>
0023 #include <linux/types.h>
0024 
0025 #define NUM_PWM     4
0026 
0027 /* PWM registers and bits definitions */
0028 #define PWMCR           0x00    /* Control Register */
0029 #define PWMCR_PWM_ENABLE    0x1
0030 #define PWMCR_PRESCALE_SHIFT    2
0031 #define PWMCR_MIN_PRESCALE  0x00
0032 #define PWMCR_MAX_PRESCALE  0x3FFF
0033 
0034 #define PWMDCR          0x04    /* Duty Cycle Register */
0035 #define PWMDCR_MIN_DUTY     0x0001
0036 #define PWMDCR_MAX_DUTY     0xFFFF
0037 
0038 #define PWMPCR          0x08    /* Period Register */
0039 #define PWMPCR_MIN_PERIOD   0x0001
0040 #define PWMPCR_MAX_PERIOD   0xFFFF
0041 
0042 /* Following only available on 13xx SoCs */
0043 #define PWMMCR          0x3C    /* Master Control Register */
0044 #define PWMMCR_PWM_ENABLE   0x1
0045 
0046 /**
0047  * struct spear_pwm_chip - struct representing pwm chip
0048  *
0049  * @mmio_base: base address of pwm chip
0050  * @clk: pointer to clk structure of pwm chip
0051  * @chip: linux pwm chip representation
0052  */
0053 struct spear_pwm_chip {
0054     void __iomem *mmio_base;
0055     struct clk *clk;
0056     struct pwm_chip chip;
0057 };
0058 
0059 static inline struct spear_pwm_chip *to_spear_pwm_chip(struct pwm_chip *chip)
0060 {
0061     return container_of(chip, struct spear_pwm_chip, chip);
0062 }
0063 
0064 static inline u32 spear_pwm_readl(struct spear_pwm_chip *chip, unsigned int num,
0065                   unsigned long offset)
0066 {
0067     return readl_relaxed(chip->mmio_base + (num << 4) + offset);
0068 }
0069 
0070 static inline void spear_pwm_writel(struct spear_pwm_chip *chip,
0071                     unsigned int num, unsigned long offset,
0072                     unsigned long val)
0073 {
0074     writel_relaxed(val, chip->mmio_base + (num << 4) + offset);
0075 }
0076 
0077 static int spear_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
0078                 u64 duty_ns, u64 period_ns)
0079 {
0080     struct spear_pwm_chip *pc = to_spear_pwm_chip(chip);
0081     u64 val, div, clk_rate;
0082     unsigned long prescale = PWMCR_MIN_PRESCALE, pv, dc;
0083     int ret;
0084 
0085     /*
0086      * Find pv, dc and prescale to suit duty_ns and period_ns. This is done
0087      * according to formulas described below:
0088      *
0089      * period_ns = 10^9 * (PRESCALE + 1) * PV / PWM_CLK_RATE
0090      * duty_ns = 10^9 * (PRESCALE + 1) * DC / PWM_CLK_RATE
0091      *
0092      * PV = (PWM_CLK_RATE * period_ns) / (10^9 * (PRESCALE + 1))
0093      * DC = (PWM_CLK_RATE * duty_ns) / (10^9 * (PRESCALE + 1))
0094      */
0095     clk_rate = clk_get_rate(pc->clk);
0096     while (1) {
0097         div = 1000000000;
0098         div *= 1 + prescale;
0099         val = clk_rate * period_ns;
0100         pv = div64_u64(val, div);
0101         val = clk_rate * duty_ns;
0102         dc = div64_u64(val, div);
0103 
0104         /* if duty_ns and period_ns are not achievable then return */
0105         if (pv < PWMPCR_MIN_PERIOD || dc < PWMDCR_MIN_DUTY)
0106             return -EINVAL;
0107 
0108         /*
0109          * if pv and dc have crossed their upper limit, then increase
0110          * prescale and recalculate pv and dc.
0111          */
0112         if (pv > PWMPCR_MAX_PERIOD || dc > PWMDCR_MAX_DUTY) {
0113             if (++prescale > PWMCR_MAX_PRESCALE)
0114                 return -EINVAL;
0115             continue;
0116         }
0117         break;
0118     }
0119 
0120     /*
0121      * NOTE: the clock to PWM has to be enabled first before writing to the
0122      * registers.
0123      */
0124     ret = clk_enable(pc->clk);
0125     if (ret)
0126         return ret;
0127 
0128     spear_pwm_writel(pc, pwm->hwpwm, PWMCR,
0129             prescale << PWMCR_PRESCALE_SHIFT);
0130     spear_pwm_writel(pc, pwm->hwpwm, PWMDCR, dc);
0131     spear_pwm_writel(pc, pwm->hwpwm, PWMPCR, pv);
0132     clk_disable(pc->clk);
0133 
0134     return 0;
0135 }
0136 
0137 static int spear_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm)
0138 {
0139     struct spear_pwm_chip *pc = to_spear_pwm_chip(chip);
0140     int rc = 0;
0141     u32 val;
0142 
0143     rc = clk_enable(pc->clk);
0144     if (rc)
0145         return rc;
0146 
0147     val = spear_pwm_readl(pc, pwm->hwpwm, PWMCR);
0148     val |= PWMCR_PWM_ENABLE;
0149     spear_pwm_writel(pc, pwm->hwpwm, PWMCR, val);
0150 
0151     return 0;
0152 }
0153 
0154 static void spear_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm)
0155 {
0156     struct spear_pwm_chip *pc = to_spear_pwm_chip(chip);
0157     u32 val;
0158 
0159     val = spear_pwm_readl(pc, pwm->hwpwm, PWMCR);
0160     val &= ~PWMCR_PWM_ENABLE;
0161     spear_pwm_writel(pc, pwm->hwpwm, PWMCR, val);
0162 
0163     clk_disable(pc->clk);
0164 }
0165 
0166 static int spear_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
0167                const struct pwm_state *state)
0168 {
0169     int err;
0170 
0171     if (state->polarity != PWM_POLARITY_NORMAL)
0172         return -EINVAL;
0173 
0174     if (!state->enabled) {
0175         if (pwm->state.enabled)
0176             spear_pwm_disable(chip, pwm);
0177         return 0;
0178     }
0179 
0180     err = spear_pwm_config(chip, pwm, state->duty_cycle, state->period);
0181     if (err)
0182         return err;
0183 
0184     if (!pwm->state.enabled)
0185         return spear_pwm_enable(chip, pwm);
0186 
0187     return 0;
0188 }
0189 
0190 static const struct pwm_ops spear_pwm_ops = {
0191     .apply = spear_pwm_apply,
0192     .owner = THIS_MODULE,
0193 };
0194 
0195 static int spear_pwm_probe(struct platform_device *pdev)
0196 {
0197     struct device_node *np = pdev->dev.of_node;
0198     struct spear_pwm_chip *pc;
0199     int ret;
0200     u32 val;
0201 
0202     pc = devm_kzalloc(&pdev->dev, sizeof(*pc), GFP_KERNEL);
0203     if (!pc)
0204         return -ENOMEM;
0205 
0206     pc->mmio_base = devm_platform_ioremap_resource(pdev, 0);
0207     if (IS_ERR(pc->mmio_base))
0208         return PTR_ERR(pc->mmio_base);
0209 
0210     pc->clk = devm_clk_get(&pdev->dev, NULL);
0211     if (IS_ERR(pc->clk))
0212         return PTR_ERR(pc->clk);
0213 
0214     platform_set_drvdata(pdev, pc);
0215 
0216     pc->chip.dev = &pdev->dev;
0217     pc->chip.ops = &spear_pwm_ops;
0218     pc->chip.npwm = NUM_PWM;
0219 
0220     ret = clk_prepare(pc->clk);
0221     if (ret)
0222         return ret;
0223 
0224     if (of_device_is_compatible(np, "st,spear1340-pwm")) {
0225         ret = clk_enable(pc->clk);
0226         if (ret) {
0227             clk_unprepare(pc->clk);
0228             return ret;
0229         }
0230         /*
0231          * Following enables PWM chip, channels would still be
0232          * enabled individually through their control register
0233          */
0234         val = readl_relaxed(pc->mmio_base + PWMMCR);
0235         val |= PWMMCR_PWM_ENABLE;
0236         writel_relaxed(val, pc->mmio_base + PWMMCR);
0237 
0238         clk_disable(pc->clk);
0239     }
0240 
0241     ret = pwmchip_add(&pc->chip);
0242     if (ret < 0) {
0243         clk_unprepare(pc->clk);
0244         dev_err(&pdev->dev, "pwmchip_add() failed: %d\n", ret);
0245     }
0246 
0247     return ret;
0248 }
0249 
0250 static int spear_pwm_remove(struct platform_device *pdev)
0251 {
0252     struct spear_pwm_chip *pc = platform_get_drvdata(pdev);
0253 
0254     pwmchip_remove(&pc->chip);
0255 
0256     /* clk was prepared in probe, hence unprepare it here */
0257     clk_unprepare(pc->clk);
0258 
0259     return 0;
0260 }
0261 
0262 static const struct of_device_id spear_pwm_of_match[] = {
0263     { .compatible = "st,spear320-pwm" },
0264     { .compatible = "st,spear1340-pwm" },
0265     { }
0266 };
0267 
0268 MODULE_DEVICE_TABLE(of, spear_pwm_of_match);
0269 
0270 static struct platform_driver spear_pwm_driver = {
0271     .driver = {
0272         .name = "spear-pwm",
0273         .of_match_table = spear_pwm_of_match,
0274     },
0275     .probe = spear_pwm_probe,
0276     .remove = spear_pwm_remove,
0277 };
0278 
0279 module_platform_driver(spear_pwm_driver);
0280 
0281 MODULE_LICENSE("GPL");
0282 MODULE_AUTHOR("Shiraz Hashim <shiraz.linux.kernel@gmail.com>");
0283 MODULE_AUTHOR("Viresh Kumar <viresh.kumar@linaro.com>");
0284 MODULE_ALIAS("platform:spear-pwm");