uboot/drivers/pwm/pwm-sifive.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0+
   2/*
   3 * Copyright (C) 2020 SiFive, Inc
   4 * For SiFive's PWM IP block documentation please refer Chapter 14 of
   5 * Reference Manual : https://static.dev.sifive.com/FU540-C000-v1.0.pdf
   6 *
   7 * Limitations:
   8 * - When changing both duty cycle and period, we cannot prevent in
   9 *   software that the output might produce a period with mixed
  10 *   settings (new period length and old duty cycle).
  11 * - The hardware cannot generate a 100% duty cycle.
  12 * - The hardware generates only inverted output.
  13 */
  14
  15#include <common.h>
  16#include <clk.h>
  17#include <div64.h>
  18#include <dm.h>
  19#include <pwm.h>
  20#include <regmap.h>
  21#include <asm/global_data.h>
  22#include <linux/io.h>
  23#include <linux/log2.h>
  24#include <linux/bitfield.h>
  25
  26/* PWMCFG fields */
  27#define PWM_SIFIVE_PWMCFG_SCALE         GENMASK(3, 0)
  28#define PWM_SIFIVE_PWMCFG_STICKY        BIT(8)
  29#define PWM_SIFIVE_PWMCFG_ZERO_CMP      BIT(9)
  30#define PWM_SIFIVE_PWMCFG_DEGLITCH      BIT(10)
  31#define PWM_SIFIVE_PWMCFG_EN_ALWAYS     BIT(12)
  32#define PWM_SIFIVE_PWMCFG_EN_ONCE       BIT(13)
  33#define PWM_SIFIVE_PWMCFG_CENTER        BIT(16)
  34#define PWM_SIFIVE_PWMCFG_GANG          BIT(24)
  35#define PWM_SIFIVE_PWMCFG_IP            BIT(28)
  36
  37/* PWM_SIFIVE_SIZE_PWMCMP is used to calculate offset for pwmcmpX registers */
  38#define PWM_SIFIVE_SIZE_PWMCMP          4
  39#define PWM_SIFIVE_CMPWIDTH             16
  40
  41DECLARE_GLOBAL_DATA_PTR;
  42
  43struct pwm_sifive_regs {
  44        unsigned long cfg;
  45        unsigned long cnt;
  46        unsigned long pwms;
  47        unsigned long cmp0;
  48};
  49
  50struct pwm_sifive_data {
  51        struct pwm_sifive_regs regs;
  52};
  53
  54struct pwm_sifive_priv {
  55        void __iomem *base;
  56        ulong freq;
  57        const struct pwm_sifive_data *data;
  58};
  59
  60static int pwm_sifive_set_config(struct udevice *dev, uint channel,
  61                                 uint period_ns, uint duty_ns)
  62{
  63        struct pwm_sifive_priv *priv = dev_get_priv(dev);
  64        const struct pwm_sifive_regs *regs = &priv->data->regs;
  65        unsigned long scale_pow;
  66        unsigned long long num;
  67        u32 scale, val = 0, frac;
  68
  69        debug("%s: period_ns=%u, duty_ns=%u\n", __func__, period_ns, duty_ns);
  70
  71        /*
  72         * The PWM unit is used with pwmzerocmp=0, so the only way to modify the
  73         * period length is using pwmscale which provides the number of bits the
  74         * counter is shifted before being feed to the comparators. A period
  75         * lasts (1 << (PWM_SIFIVE_CMPWIDTH + pwmscale)) clock ticks.
  76         * (1 << (PWM_SIFIVE_CMPWIDTH + scale)) * 10^9/rate = period
  77         */
  78        scale_pow = lldiv((uint64_t)priv->freq * period_ns, 1000000000);
  79        scale = clamp(ilog2(scale_pow) - PWM_SIFIVE_CMPWIDTH, 0, 0xf);
  80        val |= FIELD_PREP(PWM_SIFIVE_PWMCFG_SCALE, scale);
  81
  82        /*
  83         * The problem of output producing mixed setting as mentioned at top,
  84         * occurs here. To minimize the window for this problem, we are
  85         * calculating the register values first and then writing them
  86         * consecutively
  87         */
  88        num = (u64)duty_ns * (1U << PWM_SIFIVE_CMPWIDTH);
  89        frac = DIV_ROUND_CLOSEST_ULL(num, period_ns);
  90        frac = min(frac, (1U << PWM_SIFIVE_CMPWIDTH) - 1);
  91
  92        writel(val, priv->base + regs->cfg);
  93        writel(frac, priv->base + regs->cmp0 + channel *
  94               PWM_SIFIVE_SIZE_PWMCMP);
  95
  96        return 0;
  97}
  98
  99static int pwm_sifive_set_enable(struct udevice *dev, uint channel, bool enable)
 100{
 101        struct pwm_sifive_priv *priv = dev_get_priv(dev);
 102        const struct pwm_sifive_regs *regs = &priv->data->regs;
 103        u32 val;
 104
 105        debug("%s: Enable '%s'\n", __func__, dev->name);
 106
 107        if (enable) {
 108                val = readl(priv->base + regs->cfg);
 109                val |= PWM_SIFIVE_PWMCFG_EN_ALWAYS;
 110                writel(val, priv->base + regs->cfg);
 111        } else {
 112                writel(0, priv->base + regs->cmp0 + channel *
 113                       PWM_SIFIVE_SIZE_PWMCMP);
 114        }
 115
 116        return 0;
 117}
 118
 119static int pwm_sifive_of_to_plat(struct udevice *dev)
 120{
 121        struct pwm_sifive_priv *priv = dev_get_priv(dev);
 122
 123        priv->base = dev_read_addr_ptr(dev);
 124
 125        return 0;
 126}
 127
 128static int pwm_sifive_probe(struct udevice *dev)
 129{
 130        struct pwm_sifive_priv *priv = dev_get_priv(dev);
 131        struct clk clk;
 132        int ret = 0;
 133
 134        ret = clk_get_by_index(dev, 0, &clk);
 135        if (ret < 0) {
 136                debug("%s get clock fail!\n", __func__);
 137                return -EINVAL;
 138        }
 139
 140        priv->freq = clk_get_rate(&clk);
 141        priv->data = (struct pwm_sifive_data *)dev_get_driver_data(dev);
 142
 143        return 0;
 144}
 145
 146static const struct pwm_ops pwm_sifive_ops = {
 147        .set_config     = pwm_sifive_set_config,
 148        .set_enable     = pwm_sifive_set_enable,
 149};
 150
 151static const struct pwm_sifive_data pwm_data = {
 152        .regs = {
 153                .cfg = 0x00,
 154                .cnt = 0x08,
 155                .pwms = 0x10,
 156                .cmp0 = 0x20,
 157        },
 158};
 159
 160static const struct udevice_id pwm_sifive_ids[] = {
 161        { .compatible = "sifive,pwm0", .data = (ulong)&pwm_data},
 162        { }
 163};
 164
 165U_BOOT_DRIVER(pwm_sifive) = {
 166        .name   = "pwm_sifive",
 167        .id     = UCLASS_PWM,
 168        .of_match = pwm_sifive_ids,
 169        .ops    = &pwm_sifive_ops,
 170        .of_to_plat     = pwm_sifive_of_to_plat,
 171        .probe          = pwm_sifive_probe,
 172        .priv_auto      = sizeof(struct pwm_sifive_priv),
 173};
 174