310 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * Copyright (C) 2014 Free Electrons4 * Copyright (C) 2014 Atmel5 *6 * Author: Boris BREZILLON <boris.brezillon@free-electrons.com>7 */8 9#include <linux/clk.h>10#include <linux/delay.h>11#include <linux/mfd/atmel-hlcdc.h>12#include <linux/module.h>13#include <linux/of.h>14#include <linux/platform_device.h>15#include <linux/pwm.h>16#include <linux/regmap.h>17 18#define ATMEL_HLCDC_PWMCVAL_MASK GENMASK(15, 8)19#define ATMEL_HLCDC_PWMCVAL(x) (((x) << 8) & ATMEL_HLCDC_PWMCVAL_MASK)20#define ATMEL_HLCDC_PWMPOL BIT(4)21#define ATMEL_HLCDC_PWMPS_MASK GENMASK(2, 0)22#define ATMEL_HLCDC_PWMPS_MAX 0x623#define ATMEL_HLCDC_PWMPS(x) ((x) & ATMEL_HLCDC_PWMPS_MASK)24 25struct atmel_hlcdc_pwm_errata {26 bool slow_clk_erratum;27 bool div1_clk_erratum;28};29 30struct atmel_hlcdc_pwm {31 struct atmel_hlcdc *hlcdc;32 struct clk *cur_clk;33 const struct atmel_hlcdc_pwm_errata *errata;34};35 36static inline struct atmel_hlcdc_pwm *to_atmel_hlcdc_pwm(struct pwm_chip *chip)37{38 return pwmchip_get_drvdata(chip);39}40 41static int atmel_hlcdc_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,42 const struct pwm_state *state)43{44 struct atmel_hlcdc_pwm *atmel = to_atmel_hlcdc_pwm(chip);45 struct atmel_hlcdc *hlcdc = atmel->hlcdc;46 unsigned int status;47 int ret;48 49 if (state->enabled) {50 struct clk *new_clk = hlcdc->slow_clk;51 u64 pwmcval = state->duty_cycle * 256;52 unsigned long clk_freq;53 u64 clk_period_ns;54 u32 pwmcfg;55 int pres;56 57 if (!atmel->errata || !atmel->errata->slow_clk_erratum) {58 clk_freq = clk_get_rate(new_clk);59 if (!clk_freq)60 return -EINVAL;61 62 clk_period_ns = (u64)NSEC_PER_SEC * 256;63 do_div(clk_period_ns, clk_freq);64 }65 66 /* Errata: cannot use slow clk on some IP revisions */67 if ((atmel->errata && atmel->errata->slow_clk_erratum) ||68 clk_period_ns > state->period) {69 new_clk = hlcdc->sys_clk;70 clk_freq = clk_get_rate(new_clk);71 if (!clk_freq)72 return -EINVAL;73 74 clk_period_ns = (u64)NSEC_PER_SEC * 256;75 do_div(clk_period_ns, clk_freq);76 }77 78 for (pres = 0; pres <= ATMEL_HLCDC_PWMPS_MAX; pres++) {79 /* Errata: cannot divide by 1 on some IP revisions */80 if (!pres && atmel->errata &&81 atmel->errata->div1_clk_erratum)82 continue;83 84 if ((clk_period_ns << pres) >= state->period)85 break;86 }87 88 if (pres > ATMEL_HLCDC_PWMPS_MAX)89 return -EINVAL;90 91 pwmcfg = ATMEL_HLCDC_PWMPS(pres);92 93 if (new_clk != atmel->cur_clk) {94 u32 gencfg = 0;95 int ret;96 97 ret = clk_prepare_enable(new_clk);98 if (ret)99 return ret;100 101 clk_disable_unprepare(atmel->cur_clk);102 atmel->cur_clk = new_clk;103 104 if (new_clk == hlcdc->sys_clk)105 gencfg = ATMEL_HLCDC_CLKPWMSEL;106 107 ret = regmap_update_bits(hlcdc->regmap,108 ATMEL_HLCDC_CFG(0),109 ATMEL_HLCDC_CLKPWMSEL,110 gencfg);111 if (ret)112 return ret;113 }114 115 do_div(pwmcval, state->period);116 117 /*118 * The PWM duty cycle is configurable from 0/256 to 255/256 of119 * the period cycle. Hence we can't set a duty cycle occupying120 * the whole period cycle if we're asked to.121 * Set it to 255 if pwmcval is greater than 256.122 */123 if (pwmcval > 255)124 pwmcval = 255;125 126 pwmcfg |= ATMEL_HLCDC_PWMCVAL(pwmcval);127 128 if (state->polarity == PWM_POLARITY_NORMAL)129 pwmcfg |= ATMEL_HLCDC_PWMPOL;130 131 ret = regmap_update_bits(hlcdc->regmap, ATMEL_HLCDC_CFG(6),132 ATMEL_HLCDC_PWMCVAL_MASK |133 ATMEL_HLCDC_PWMPS_MASK |134 ATMEL_HLCDC_PWMPOL,135 pwmcfg);136 if (ret)137 return ret;138 139 ret = regmap_write(hlcdc->regmap, ATMEL_HLCDC_EN,140 ATMEL_HLCDC_PWM);141 if (ret)142 return ret;143 144 ret = regmap_read_poll_timeout(hlcdc->regmap, ATMEL_HLCDC_SR,145 status,146 status & ATMEL_HLCDC_PWM,147 10, 0);148 if (ret)149 return ret;150 } else {151 ret = regmap_write(hlcdc->regmap, ATMEL_HLCDC_DIS,152 ATMEL_HLCDC_PWM);153 if (ret)154 return ret;155 156 ret = regmap_read_poll_timeout(hlcdc->regmap, ATMEL_HLCDC_SR,157 status,158 !(status & ATMEL_HLCDC_PWM),159 10, 0);160 if (ret)161 return ret;162 163 clk_disable_unprepare(atmel->cur_clk);164 atmel->cur_clk = NULL;165 }166 167 return 0;168}169 170static const struct pwm_ops atmel_hlcdc_pwm_ops = {171 .apply = atmel_hlcdc_pwm_apply,172};173 174static const struct atmel_hlcdc_pwm_errata atmel_hlcdc_pwm_at91sam9x5_errata = {175 .slow_clk_erratum = true,176};177 178static const struct atmel_hlcdc_pwm_errata atmel_hlcdc_pwm_sama5d3_errata = {179 .div1_clk_erratum = true,180};181 182static int atmel_hlcdc_pwm_suspend(struct device *dev)183{184 struct pwm_chip *chip = dev_get_drvdata(dev);185 struct atmel_hlcdc_pwm *atmel = to_atmel_hlcdc_pwm(chip);186 struct pwm_device *pwm = &chip->pwms[0];187 188 /* Keep the periph clock enabled if the PWM is still running. */189 if (!pwm->state.enabled)190 clk_disable_unprepare(atmel->hlcdc->periph_clk);191 192 return 0;193}194 195static int atmel_hlcdc_pwm_resume(struct device *dev)196{197 struct pwm_chip *chip = dev_get_drvdata(dev);198 struct atmel_hlcdc_pwm *atmel = to_atmel_hlcdc_pwm(chip);199 struct pwm_device *pwm = &chip->pwms[0];200 int ret;201 202 /* Re-enable the periph clock it was stopped during suspend. */203 if (!pwm->state.enabled) {204 ret = clk_prepare_enable(atmel->hlcdc->periph_clk);205 if (ret)206 return ret;207 }208 209 return atmel_hlcdc_pwm_apply(chip, pwm, &pwm->state);210}211 212static DEFINE_SIMPLE_DEV_PM_OPS(atmel_hlcdc_pwm_pm_ops,213 atmel_hlcdc_pwm_suspend, atmel_hlcdc_pwm_resume);214 215static const struct of_device_id atmel_hlcdc_dt_ids[] = {216 {217 .compatible = "atmel,at91sam9n12-hlcdc",218 /* 9n12 has same errata as 9x5 HLCDC PWM */219 .data = &atmel_hlcdc_pwm_at91sam9x5_errata,220 },221 {222 .compatible = "atmel,at91sam9x5-hlcdc",223 .data = &atmel_hlcdc_pwm_at91sam9x5_errata,224 },225 {226 .compatible = "atmel,sama5d2-hlcdc",227 },228 {229 .compatible = "atmel,sama5d3-hlcdc",230 .data = &atmel_hlcdc_pwm_sama5d3_errata,231 },232 {233 .compatible = "atmel,sama5d4-hlcdc",234 .data = &atmel_hlcdc_pwm_sama5d3_errata,235 },236 { .compatible = "microchip,sam9x60-hlcdc", },237 { /* sentinel */ }238};239MODULE_DEVICE_TABLE(of, atmel_hlcdc_dt_ids);240 241static int atmel_hlcdc_pwm_probe(struct platform_device *pdev)242{243 const struct of_device_id *match;244 struct device *dev = &pdev->dev;245 struct pwm_chip *chip;246 struct atmel_hlcdc_pwm *atmel;247 struct atmel_hlcdc *hlcdc;248 int ret;249 250 hlcdc = dev_get_drvdata(dev->parent);251 252 chip = devm_pwmchip_alloc(dev, 1, sizeof(*atmel));253 if (IS_ERR(chip))254 return PTR_ERR(chip);255 atmel = to_atmel_hlcdc_pwm(chip);256 257 ret = clk_prepare_enable(hlcdc->periph_clk);258 if (ret)259 return ret;260 261 match = of_match_node(atmel_hlcdc_dt_ids, dev->parent->of_node);262 if (match)263 atmel->errata = match->data;264 265 atmel->hlcdc = hlcdc;266 chip->ops = &atmel_hlcdc_pwm_ops;267 268 ret = pwmchip_add(chip);269 if (ret) {270 clk_disable_unprepare(hlcdc->periph_clk);271 return ret;272 }273 274 platform_set_drvdata(pdev, chip);275 276 return 0;277}278 279static void atmel_hlcdc_pwm_remove(struct platform_device *pdev)280{281 struct pwm_chip *chip = platform_get_drvdata(pdev);282 struct atmel_hlcdc_pwm *atmel = to_atmel_hlcdc_pwm(chip);283 284 pwmchip_remove(chip);285 286 clk_disable_unprepare(atmel->hlcdc->periph_clk);287}288 289static const struct of_device_id atmel_hlcdc_pwm_dt_ids[] = {290 { .compatible = "atmel,hlcdc-pwm" },291 { /* sentinel */ }292};293MODULE_DEVICE_TABLE(of, atmel_hlcdc_pwm_dt_ids);294 295static struct platform_driver atmel_hlcdc_pwm_driver = {296 .driver = {297 .name = "atmel-hlcdc-pwm",298 .of_match_table = atmel_hlcdc_pwm_dt_ids,299 .pm = pm_ptr(&atmel_hlcdc_pwm_pm_ops),300 },301 .probe = atmel_hlcdc_pwm_probe,302 .remove = atmel_hlcdc_pwm_remove,303};304module_platform_driver(atmel_hlcdc_pwm_driver);305 306MODULE_ALIAS("platform:atmel-hlcdc-pwm");307MODULE_AUTHOR("Boris Brezillon <boris.brezillon@free-electrons.com>");308MODULE_DESCRIPTION("Atmel HLCDC PWM driver");309MODULE_LICENSE("GPL v2");310