Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /*
0003  * Copyright (C) 2014 Free Electrons
0004  * Copyright (C) 2014 Atmel
0005  *
0006  * Author: Boris BREZILLON <boris.brezillon@free-electrons.com>
0007  */
0008 
0009 #include <linux/clk.h>
0010 #include <linux/delay.h>
0011 #include <linux/mfd/atmel-hlcdc.h>
0012 #include <linux/module.h>
0013 #include <linux/platform_device.h>
0014 #include <linux/pwm.h>
0015 #include <linux/regmap.h>
0016 
0017 #define ATMEL_HLCDC_PWMCVAL_MASK    GENMASK(15, 8)
0018 #define ATMEL_HLCDC_PWMCVAL(x)      (((x) << 8) & ATMEL_HLCDC_PWMCVAL_MASK)
0019 #define ATMEL_HLCDC_PWMPOL      BIT(4)
0020 #define ATMEL_HLCDC_PWMPS_MASK      GENMASK(2, 0)
0021 #define ATMEL_HLCDC_PWMPS_MAX       0x6
0022 #define ATMEL_HLCDC_PWMPS(x)        ((x) & ATMEL_HLCDC_PWMPS_MASK)
0023 
0024 struct atmel_hlcdc_pwm_errata {
0025     bool slow_clk_erratum;
0026     bool div1_clk_erratum;
0027 };
0028 
0029 struct atmel_hlcdc_pwm {
0030     struct pwm_chip chip;
0031     struct atmel_hlcdc *hlcdc;
0032     struct clk *cur_clk;
0033     const struct atmel_hlcdc_pwm_errata *errata;
0034 };
0035 
0036 static inline struct atmel_hlcdc_pwm *to_atmel_hlcdc_pwm(struct pwm_chip *chip)
0037 {
0038     return container_of(chip, struct atmel_hlcdc_pwm, chip);
0039 }
0040 
0041 static int atmel_hlcdc_pwm_apply(struct pwm_chip *c, struct pwm_device *pwm,
0042                  const struct pwm_state *state)
0043 {
0044     struct atmel_hlcdc_pwm *chip = to_atmel_hlcdc_pwm(c);
0045     struct atmel_hlcdc *hlcdc = chip->hlcdc;
0046     unsigned int status;
0047     int ret;
0048 
0049     if (state->enabled) {
0050         struct clk *new_clk = hlcdc->slow_clk;
0051         u64 pwmcval = state->duty_cycle * 256;
0052         unsigned long clk_freq;
0053         u64 clk_period_ns;
0054         u32 pwmcfg;
0055         int pres;
0056 
0057         if (!chip->errata || !chip->errata->slow_clk_erratum) {
0058             clk_freq = clk_get_rate(new_clk);
0059             if (!clk_freq)
0060                 return -EINVAL;
0061 
0062             clk_period_ns = (u64)NSEC_PER_SEC * 256;
0063             do_div(clk_period_ns, clk_freq);
0064         }
0065 
0066         /* Errata: cannot use slow clk on some IP revisions */
0067         if ((chip->errata && chip->errata->slow_clk_erratum) ||
0068             clk_period_ns > state->period) {
0069             new_clk = hlcdc->sys_clk;
0070             clk_freq = clk_get_rate(new_clk);
0071             if (!clk_freq)
0072                 return -EINVAL;
0073 
0074             clk_period_ns = (u64)NSEC_PER_SEC * 256;
0075             do_div(clk_period_ns, clk_freq);
0076         }
0077 
0078         for (pres = 0; pres <= ATMEL_HLCDC_PWMPS_MAX; pres++) {
0079         /* Errata: cannot divide by 1 on some IP revisions */
0080             if (!pres && chip->errata &&
0081                 chip->errata->div1_clk_erratum)
0082                 continue;
0083 
0084             if ((clk_period_ns << pres) >= state->period)
0085                 break;
0086         }
0087 
0088         if (pres > ATMEL_HLCDC_PWMPS_MAX)
0089             return -EINVAL;
0090 
0091         pwmcfg = ATMEL_HLCDC_PWMPS(pres);
0092 
0093         if (new_clk != chip->cur_clk) {
0094             u32 gencfg = 0;
0095             int ret;
0096 
0097             ret = clk_prepare_enable(new_clk);
0098             if (ret)
0099                 return ret;
0100 
0101             clk_disable_unprepare(chip->cur_clk);
0102             chip->cur_clk = new_clk;
0103 
0104             if (new_clk == hlcdc->sys_clk)
0105                 gencfg = ATMEL_HLCDC_CLKPWMSEL;
0106 
0107             ret = regmap_update_bits(hlcdc->regmap,
0108                          ATMEL_HLCDC_CFG(0),
0109                          ATMEL_HLCDC_CLKPWMSEL,
0110                          gencfg);
0111             if (ret)
0112                 return ret;
0113         }
0114 
0115         do_div(pwmcval, state->period);
0116 
0117         /*
0118          * The PWM duty cycle is configurable from 0/256 to 255/256 of
0119          * the period cycle. Hence we can't set a duty cycle occupying
0120          * the whole period cycle if we're asked to.
0121          * Set it to 255 if pwmcval is greater than 256.
0122          */
0123         if (pwmcval > 255)
0124             pwmcval = 255;
0125 
0126         pwmcfg |= ATMEL_HLCDC_PWMCVAL(pwmcval);
0127 
0128         if (state->polarity == PWM_POLARITY_NORMAL)
0129             pwmcfg |= ATMEL_HLCDC_PWMPOL;
0130 
0131         ret = regmap_update_bits(hlcdc->regmap, ATMEL_HLCDC_CFG(6),
0132                      ATMEL_HLCDC_PWMCVAL_MASK |
0133                      ATMEL_HLCDC_PWMPS_MASK |
0134                      ATMEL_HLCDC_PWMPOL,
0135                      pwmcfg);
0136         if (ret)
0137             return ret;
0138 
0139         ret = regmap_write(hlcdc->regmap, ATMEL_HLCDC_EN,
0140                    ATMEL_HLCDC_PWM);
0141         if (ret)
0142             return ret;
0143 
0144         ret = regmap_read_poll_timeout(hlcdc->regmap, ATMEL_HLCDC_SR,
0145                            status,
0146                            status & ATMEL_HLCDC_PWM,
0147                            10, 0);
0148         if (ret)
0149             return ret;
0150     } else {
0151         ret = regmap_write(hlcdc->regmap, ATMEL_HLCDC_DIS,
0152                    ATMEL_HLCDC_PWM);
0153         if (ret)
0154             return ret;
0155 
0156         ret = regmap_read_poll_timeout(hlcdc->regmap, ATMEL_HLCDC_SR,
0157                            status,
0158                            !(status & ATMEL_HLCDC_PWM),
0159                            10, 0);
0160         if (ret)
0161             return ret;
0162 
0163         clk_disable_unprepare(chip->cur_clk);
0164         chip->cur_clk = NULL;
0165     }
0166 
0167     return 0;
0168 }
0169 
0170 static const struct pwm_ops atmel_hlcdc_pwm_ops = {
0171     .apply = atmel_hlcdc_pwm_apply,
0172     .owner = THIS_MODULE,
0173 };
0174 
0175 static const struct atmel_hlcdc_pwm_errata atmel_hlcdc_pwm_at91sam9x5_errata = {
0176     .slow_clk_erratum = true,
0177 };
0178 
0179 static const struct atmel_hlcdc_pwm_errata atmel_hlcdc_pwm_sama5d3_errata = {
0180     .div1_clk_erratum = true,
0181 };
0182 
0183 #ifdef CONFIG_PM_SLEEP
0184 static int atmel_hlcdc_pwm_suspend(struct device *dev)
0185 {
0186     struct atmel_hlcdc_pwm *chip = dev_get_drvdata(dev);
0187 
0188     /* Keep the periph clock enabled if the PWM is still running. */
0189     if (pwm_is_enabled(&chip->chip.pwms[0]))
0190         clk_disable_unprepare(chip->hlcdc->periph_clk);
0191 
0192     return 0;
0193 }
0194 
0195 static int atmel_hlcdc_pwm_resume(struct device *dev)
0196 {
0197     struct atmel_hlcdc_pwm *chip = dev_get_drvdata(dev);
0198     struct pwm_state state;
0199     int ret;
0200 
0201     pwm_get_state(&chip->chip.pwms[0], &state);
0202 
0203     /* Re-enable the periph clock it was stopped during suspend. */
0204     if (!state.enabled) {
0205         ret = clk_prepare_enable(chip->hlcdc->periph_clk);
0206         if (ret)
0207             return ret;
0208     }
0209 
0210     return atmel_hlcdc_pwm_apply(&chip->chip, &chip->chip.pwms[0], &state);
0211 }
0212 #endif
0213 
0214 static SIMPLE_DEV_PM_OPS(atmel_hlcdc_pwm_pm_ops,
0215              atmel_hlcdc_pwm_suspend, atmel_hlcdc_pwm_resume);
0216 
0217 static const struct of_device_id atmel_hlcdc_dt_ids[] = {
0218     {
0219         .compatible = "atmel,at91sam9n12-hlcdc",
0220         /* 9n12 has same errata as 9x5 HLCDC PWM */
0221         .data = &atmel_hlcdc_pwm_at91sam9x5_errata,
0222     },
0223     {
0224         .compatible = "atmel,at91sam9x5-hlcdc",
0225         .data = &atmel_hlcdc_pwm_at91sam9x5_errata,
0226     },
0227     {
0228         .compatible = "atmel,sama5d2-hlcdc",
0229     },
0230     {
0231         .compatible = "atmel,sama5d3-hlcdc",
0232         .data = &atmel_hlcdc_pwm_sama5d3_errata,
0233     },
0234     {
0235         .compatible = "atmel,sama5d4-hlcdc",
0236         .data = &atmel_hlcdc_pwm_sama5d3_errata,
0237     },
0238     {   .compatible = "microchip,sam9x60-hlcdc", },
0239     { /* sentinel */ },
0240 };
0241 MODULE_DEVICE_TABLE(of, atmel_hlcdc_dt_ids);
0242 
0243 static int atmel_hlcdc_pwm_probe(struct platform_device *pdev)
0244 {
0245     const struct of_device_id *match;
0246     struct device *dev = &pdev->dev;
0247     struct atmel_hlcdc_pwm *chip;
0248     struct atmel_hlcdc *hlcdc;
0249     int ret;
0250 
0251     hlcdc = dev_get_drvdata(dev->parent);
0252 
0253     chip = devm_kzalloc(dev, sizeof(*chip), GFP_KERNEL);
0254     if (!chip)
0255         return -ENOMEM;
0256 
0257     ret = clk_prepare_enable(hlcdc->periph_clk);
0258     if (ret)
0259         return ret;
0260 
0261     match = of_match_node(atmel_hlcdc_dt_ids, dev->parent->of_node);
0262     if (match)
0263         chip->errata = match->data;
0264 
0265     chip->hlcdc = hlcdc;
0266     chip->chip.ops = &atmel_hlcdc_pwm_ops;
0267     chip->chip.dev = dev;
0268     chip->chip.npwm = 1;
0269 
0270     ret = pwmchip_add(&chip->chip);
0271     if (ret) {
0272         clk_disable_unprepare(hlcdc->periph_clk);
0273         return ret;
0274     }
0275 
0276     platform_set_drvdata(pdev, chip);
0277 
0278     return 0;
0279 }
0280 
0281 static int atmel_hlcdc_pwm_remove(struct platform_device *pdev)
0282 {
0283     struct atmel_hlcdc_pwm *chip = platform_get_drvdata(pdev);
0284 
0285     pwmchip_remove(&chip->chip);
0286 
0287     clk_disable_unprepare(chip->hlcdc->periph_clk);
0288 
0289     return 0;
0290 }
0291 
0292 static const struct of_device_id atmel_hlcdc_pwm_dt_ids[] = {
0293     { .compatible = "atmel,hlcdc-pwm" },
0294     { /* sentinel */ },
0295 };
0296 
0297 static struct platform_driver atmel_hlcdc_pwm_driver = {
0298     .driver = {
0299         .name = "atmel-hlcdc-pwm",
0300         .of_match_table = atmel_hlcdc_pwm_dt_ids,
0301         .pm = &atmel_hlcdc_pwm_pm_ops,
0302     },
0303     .probe = atmel_hlcdc_pwm_probe,
0304     .remove = atmel_hlcdc_pwm_remove,
0305 };
0306 module_platform_driver(atmel_hlcdc_pwm_driver);
0307 
0308 MODULE_ALIAS("platform:atmel-hlcdc-pwm");
0309 MODULE_AUTHOR("Boris Brezillon <boris.brezillon@free-electrons.com>");
0310 MODULE_DESCRIPTION("Atmel HLCDC PWM driver");
0311 MODULE_LICENSE("GPL v2");