123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353 |
- // SPDX-License-Identifier: GPL-2.0
- /*
- * Copyright (C) 2017-2018 SiFive
- * For SiFive's PWM IP block documentation please refer Chapter 14 of
- * Reference Manual : https://static.dev.sifive.com/FU540-C000-v1.0.pdf
- *
- * Limitations:
- * - When changing both duty cycle and period, we cannot prevent in
- * software that the output might produce a period with mixed
- * settings (new period length and old duty cycle).
- * - The hardware cannot generate a 100% duty cycle.
- * - The hardware generates only inverted output.
- */
- #include <linux/clk.h>
- #include <linux/io.h>
- #include <linux/module.h>
- #include <linux/platform_device.h>
- #include <linux/pwm.h>
- #include <linux/slab.h>
- #include <linux/bitfield.h>
- /* Register offsets */
- #define PWM_SIFIVE_PWMCFG 0x0
- #define PWM_SIFIVE_PWMCOUNT 0x8
- #define PWM_SIFIVE_PWMS 0x10
- #define PWM_SIFIVE_PWMCMP(i) (0x20 + 4 * (i))
- /* PWMCFG fields */
- #define PWM_SIFIVE_PWMCFG_SCALE GENMASK(3, 0)
- #define PWM_SIFIVE_PWMCFG_STICKY BIT(8)
- #define PWM_SIFIVE_PWMCFG_ZERO_CMP BIT(9)
- #define PWM_SIFIVE_PWMCFG_DEGLITCH BIT(10)
- #define PWM_SIFIVE_PWMCFG_EN_ALWAYS BIT(12)
- #define PWM_SIFIVE_PWMCFG_EN_ONCE BIT(13)
- #define PWM_SIFIVE_PWMCFG_CENTER BIT(16)
- #define PWM_SIFIVE_PWMCFG_GANG BIT(24)
- #define PWM_SIFIVE_PWMCFG_IP BIT(28)
- #define PWM_SIFIVE_CMPWIDTH 16
- #define PWM_SIFIVE_DEFAULT_PERIOD 10000000
- struct pwm_sifive_ddata {
- struct pwm_chip chip;
- struct mutex lock; /* lock to protect user_count and approx_period */
- struct notifier_block notifier;
- struct clk *clk;
- void __iomem *regs;
- unsigned int real_period;
- unsigned int approx_period;
- int user_count;
- };
- static inline
- struct pwm_sifive_ddata *pwm_sifive_chip_to_ddata(struct pwm_chip *c)
- {
- return container_of(c, struct pwm_sifive_ddata, chip);
- }
- static int pwm_sifive_request(struct pwm_chip *chip, struct pwm_device *pwm)
- {
- struct pwm_sifive_ddata *ddata = pwm_sifive_chip_to_ddata(chip);
- mutex_lock(&ddata->lock);
- ddata->user_count++;
- mutex_unlock(&ddata->lock);
- return 0;
- }
- static void pwm_sifive_free(struct pwm_chip *chip, struct pwm_device *pwm)
- {
- struct pwm_sifive_ddata *ddata = pwm_sifive_chip_to_ddata(chip);
- mutex_lock(&ddata->lock);
- ddata->user_count--;
- mutex_unlock(&ddata->lock);
- }
- /* Called holding ddata->lock */
- static void pwm_sifive_update_clock(struct pwm_sifive_ddata *ddata,
- unsigned long rate)
- {
- unsigned long long num;
- unsigned long scale_pow;
- int scale;
- u32 val;
- /*
- * The PWM unit is used with pwmzerocmp=0, so the only way to modify the
- * period length is using pwmscale which provides the number of bits the
- * counter is shifted before being feed to the comparators. A period
- * lasts (1 << (PWM_SIFIVE_CMPWIDTH + pwmscale)) clock ticks.
- * (1 << (PWM_SIFIVE_CMPWIDTH + scale)) * 10^9/rate = period
- */
- scale_pow = div64_ul(ddata->approx_period * (u64)rate, NSEC_PER_SEC);
- scale = clamp(ilog2(scale_pow) - PWM_SIFIVE_CMPWIDTH, 0, 0xf);
- val = PWM_SIFIVE_PWMCFG_EN_ALWAYS |
- FIELD_PREP(PWM_SIFIVE_PWMCFG_SCALE, scale);
- writel(val, ddata->regs + PWM_SIFIVE_PWMCFG);
- /* As scale <= 15 the shift operation cannot overflow. */
- num = (unsigned long long)NSEC_PER_SEC << (PWM_SIFIVE_CMPWIDTH + scale);
- ddata->real_period = div64_ul(num, rate);
- dev_dbg(ddata->chip.dev,
- "New real_period = %u ns\n", ddata->real_period);
- }
- static int pwm_sifive_get_state(struct pwm_chip *chip, struct pwm_device *pwm,
- struct pwm_state *state)
- {
- struct pwm_sifive_ddata *ddata = pwm_sifive_chip_to_ddata(chip);
- u32 duty, val;
- duty = readl(ddata->regs + PWM_SIFIVE_PWMCMP(pwm->hwpwm));
- state->enabled = duty > 0;
- val = readl(ddata->regs + PWM_SIFIVE_PWMCFG);
- if (!(val & PWM_SIFIVE_PWMCFG_EN_ALWAYS))
- state->enabled = false;
- state->period = ddata->real_period;
- state->duty_cycle =
- (u64)duty * ddata->real_period >> PWM_SIFIVE_CMPWIDTH;
- state->polarity = PWM_POLARITY_INVERSED;
- return 0;
- }
- static int pwm_sifive_apply(struct pwm_chip *chip, struct pwm_device *pwm,
- const struct pwm_state *state)
- {
- struct pwm_sifive_ddata *ddata = pwm_sifive_chip_to_ddata(chip);
- struct pwm_state cur_state;
- unsigned int duty_cycle;
- unsigned long long num;
- bool enabled;
- int ret = 0;
- u32 frac;
- if (state->polarity != PWM_POLARITY_INVERSED)
- return -EINVAL;
- cur_state = pwm->state;
- enabled = cur_state.enabled;
- duty_cycle = state->duty_cycle;
- if (!state->enabled)
- duty_cycle = 0;
- /*
- * The problem of output producing mixed setting as mentioned at top,
- * occurs here. To minimize the window for this problem, we are
- * calculating the register values first and then writing them
- * consecutively
- */
- num = (u64)duty_cycle * (1U << PWM_SIFIVE_CMPWIDTH);
- frac = DIV64_U64_ROUND_CLOSEST(num, state->period);
- /* The hardware cannot generate a 100% duty cycle */
- frac = min(frac, (1U << PWM_SIFIVE_CMPWIDTH) - 1);
- mutex_lock(&ddata->lock);
- if (state->period != ddata->approx_period) {
- /*
- * Don't let a 2nd user change the period underneath the 1st user.
- * However if ddate->approx_period == 0 this is the first time we set
- * any period, so let whoever gets here first set the period so other
- * users who agree on the period won't fail.
- */
- if (ddata->user_count != 1 && ddata->approx_period) {
- mutex_unlock(&ddata->lock);
- return -EBUSY;
- }
- ddata->approx_period = state->period;
- pwm_sifive_update_clock(ddata, clk_get_rate(ddata->clk));
- }
- mutex_unlock(&ddata->lock);
- /*
- * If the PWM is enabled the clk is already on. So only enable it
- * conditionally to have it on exactly once afterwards independent of
- * the PWM state.
- */
- if (!enabled) {
- ret = clk_enable(ddata->clk);
- if (ret) {
- dev_err(ddata->chip.dev, "Enable clk failed\n");
- return ret;
- }
- }
- writel(frac, ddata->regs + PWM_SIFIVE_PWMCMP(pwm->hwpwm));
- if (!state->enabled)
- clk_disable(ddata->clk);
- return 0;
- }
- static const struct pwm_ops pwm_sifive_ops = {
- .request = pwm_sifive_request,
- .free = pwm_sifive_free,
- .get_state = pwm_sifive_get_state,
- .apply = pwm_sifive_apply,
- .owner = THIS_MODULE,
- };
- static int pwm_sifive_clock_notifier(struct notifier_block *nb,
- unsigned long event, void *data)
- {
- struct clk_notifier_data *ndata = data;
- struct pwm_sifive_ddata *ddata =
- container_of(nb, struct pwm_sifive_ddata, notifier);
- if (event == POST_RATE_CHANGE) {
- mutex_lock(&ddata->lock);
- pwm_sifive_update_clock(ddata, ndata->new_rate);
- mutex_unlock(&ddata->lock);
- }
- return NOTIFY_OK;
- }
- static int pwm_sifive_probe(struct platform_device *pdev)
- {
- struct device *dev = &pdev->dev;
- struct pwm_sifive_ddata *ddata;
- struct pwm_chip *chip;
- int ret;
- u32 val;
- unsigned int enabled_pwms = 0, enabled_clks = 1;
- ddata = devm_kzalloc(dev, sizeof(*ddata), GFP_KERNEL);
- if (!ddata)
- return -ENOMEM;
- mutex_init(&ddata->lock);
- chip = &ddata->chip;
- chip->dev = dev;
- chip->ops = &pwm_sifive_ops;
- chip->npwm = 4;
- ddata->regs = devm_platform_ioremap_resource(pdev, 0);
- if (IS_ERR(ddata->regs))
- return PTR_ERR(ddata->regs);
- ddata->clk = devm_clk_get(dev, NULL);
- if (IS_ERR(ddata->clk))
- return dev_err_probe(dev, PTR_ERR(ddata->clk),
- "Unable to find controller clock\n");
- ret = clk_prepare_enable(ddata->clk);
- if (ret) {
- dev_err(dev, "failed to enable clock for pwm: %d\n", ret);
- return ret;
- }
- val = readl(ddata->regs + PWM_SIFIVE_PWMCFG);
- if (val & PWM_SIFIVE_PWMCFG_EN_ALWAYS) {
- unsigned int i;
- for (i = 0; i < chip->npwm; ++i) {
- val = readl(ddata->regs + PWM_SIFIVE_PWMCMP(i));
- if (val > 0)
- ++enabled_pwms;
- }
- }
- /* The clk should be on once for each running PWM. */
- if (enabled_pwms) {
- while (enabled_clks < enabled_pwms) {
- /* This is not expected to fail as the clk is already on */
- ret = clk_enable(ddata->clk);
- if (unlikely(ret)) {
- dev_err_probe(dev, ret, "Failed to enable clk\n");
- goto disable_clk;
- }
- ++enabled_clks;
- }
- } else {
- clk_disable(ddata->clk);
- enabled_clks = 0;
- }
- /* Watch for changes to underlying clock frequency */
- ddata->notifier.notifier_call = pwm_sifive_clock_notifier;
- ret = clk_notifier_register(ddata->clk, &ddata->notifier);
- if (ret) {
- dev_err(dev, "failed to register clock notifier: %d\n", ret);
- goto disable_clk;
- }
- ret = pwmchip_add(chip);
- if (ret < 0) {
- dev_err(dev, "cannot register PWM: %d\n", ret);
- goto unregister_clk;
- }
- platform_set_drvdata(pdev, ddata);
- dev_dbg(dev, "SiFive PWM chip registered %d PWMs\n", chip->npwm);
- return 0;
- unregister_clk:
- clk_notifier_unregister(ddata->clk, &ddata->notifier);
- disable_clk:
- while (enabled_clks) {
- clk_disable(ddata->clk);
- --enabled_clks;
- }
- clk_unprepare(ddata->clk);
- return ret;
- }
- static int pwm_sifive_remove(struct platform_device *dev)
- {
- struct pwm_sifive_ddata *ddata = platform_get_drvdata(dev);
- struct pwm_device *pwm;
- int ch;
- pwmchip_remove(&ddata->chip);
- clk_notifier_unregister(ddata->clk, &ddata->notifier);
- for (ch = 0; ch < ddata->chip.npwm; ch++) {
- pwm = &ddata->chip.pwms[ch];
- if (pwm->state.enabled)
- clk_disable(ddata->clk);
- }
- clk_unprepare(ddata->clk);
- return 0;
- }
- static const struct of_device_id pwm_sifive_of_match[] = {
- { .compatible = "sifive,pwm0" },
- {},
- };
- MODULE_DEVICE_TABLE(of, pwm_sifive_of_match);
- static struct platform_driver pwm_sifive_driver = {
- .probe = pwm_sifive_probe,
- .remove = pwm_sifive_remove,
- .driver = {
- .name = "pwm-sifive",
- .of_match_table = pwm_sifive_of_match,
- },
- };
- module_platform_driver(pwm_sifive_driver);
- MODULE_DESCRIPTION("SiFive PWM driver");
- MODULE_LICENSE("GPL v2");
|