1 // SPDX-License-Identifier: GPL-2.0+
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
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.
22 #include <linux/log2.h>
23 #include <linux/bitfield.h>
26 #define PWM_SIFIVE_PWMCFG_SCALE GENMASK(3, 0)
27 #define PWM_SIFIVE_PWMCFG_STICKY BIT(8)
28 #define PWM_SIFIVE_PWMCFG_ZERO_CMP BIT(9)
29 #define PWM_SIFIVE_PWMCFG_DEGLITCH BIT(10)
30 #define PWM_SIFIVE_PWMCFG_EN_ALWAYS BIT(12)
31 #define PWM_SIFIVE_PWMCFG_EN_ONCE BIT(13)
32 #define PWM_SIFIVE_PWMCFG_CENTER BIT(16)
33 #define PWM_SIFIVE_PWMCFG_GANG BIT(24)
34 #define PWM_SIFIVE_PWMCFG_IP BIT(28)
36 /* PWM_SIFIVE_SIZE_PWMCMP is used to calculate offset for pwmcmpX registers */
37 #define PWM_SIFIVE_SIZE_PWMCMP 4
38 #define PWM_SIFIVE_CMPWIDTH 16
40 DECLARE_GLOBAL_DATA_PTR;
42 struct pwm_sifive_regs {
49 struct pwm_sifive_data {
50 struct pwm_sifive_regs regs;
53 struct pwm_sifive_priv {
56 const struct pwm_sifive_data *data;
59 static int pwm_sifive_set_config(struct udevice *dev, uint channel,
60 uint period_ns, uint duty_ns)
62 struct pwm_sifive_priv *priv = dev_get_priv(dev);
63 const struct pwm_sifive_regs *regs = &priv->data->regs;
64 unsigned long scale_pow;
65 unsigned long long num;
66 u32 scale, val = 0, frac;
68 debug("%s: period_ns=%u, duty_ns=%u\n", __func__, period_ns, duty_ns);
71 * The PWM unit is used with pwmzerocmp=0, so the only way to modify the
72 * period length is using pwmscale which provides the number of bits the
73 * counter is shifted before being feed to the comparators. A period
74 * lasts (1 << (PWM_SIFIVE_CMPWIDTH + pwmscale)) clock ticks.
75 * (1 << (PWM_SIFIVE_CMPWIDTH + scale)) * 10^9/rate = period
77 scale_pow = lldiv((uint64_t)priv->freq * period_ns, 1000000000);
78 scale = clamp(ilog2(scale_pow) - PWM_SIFIVE_CMPWIDTH, 0, 0xf);
79 val |= FIELD_PREP(PWM_SIFIVE_PWMCFG_SCALE, scale);
82 * The problem of output producing mixed setting as mentioned at top,
83 * occurs here. To minimize the window for this problem, we are
84 * calculating the register values first and then writing them
87 num = (u64)duty_ns * (1U << PWM_SIFIVE_CMPWIDTH);
88 frac = DIV_ROUND_CLOSEST_ULL(num, period_ns);
89 frac = min(frac, (1U << PWM_SIFIVE_CMPWIDTH) - 1);
91 writel(val, priv->base + regs->cfg);
92 writel(frac, priv->base + regs->cmp0 + channel *
93 PWM_SIFIVE_SIZE_PWMCMP);
98 static int pwm_sifive_set_enable(struct udevice *dev, uint channel, bool enable)
100 struct pwm_sifive_priv *priv = dev_get_priv(dev);
101 const struct pwm_sifive_regs *regs = &priv->data->regs;
104 debug("%s: Enable '%s'\n", __func__, dev->name);
107 val = readl(priv->base + regs->cfg);
108 val |= PWM_SIFIVE_PWMCFG_EN_ALWAYS;
109 writel(val, priv->base + regs->cfg);
111 writel(0, priv->base + regs->cmp0 + channel *
112 PWM_SIFIVE_SIZE_PWMCMP);
118 static int pwm_sifive_ofdata_to_platdata(struct udevice *dev)
120 struct pwm_sifive_priv *priv = dev_get_priv(dev);
122 priv->base = dev_read_addr_ptr(dev);
127 static int pwm_sifive_probe(struct udevice *dev)
129 struct pwm_sifive_priv *priv = dev_get_priv(dev);
133 ret = clk_get_by_index(dev, 0, &clk);
135 debug("%s get clock fail!\n", __func__);
139 priv->freq = clk_get_rate(&clk);
140 priv->data = (struct pwm_sifive_data *)dev_get_driver_data(dev);
145 static const struct pwm_ops pwm_sifive_ops = {
146 .set_config = pwm_sifive_set_config,
147 .set_enable = pwm_sifive_set_enable,
150 static const struct pwm_sifive_data pwm_data = {
159 static const struct udevice_id pwm_sifive_ids[] = {
160 { .compatible = "sifive,pwm0", .data = (ulong)&pwm_data},
164 U_BOOT_DRIVER(pwm_sifive) = {
165 .name = "pwm_sifive",
167 .of_match = pwm_sifive_ids,
168 .ops = &pwm_sifive_ops,
169 .ofdata_to_platdata = pwm_sifive_ofdata_to_platdata,
170 .probe = pwm_sifive_probe,
171 .priv_auto = sizeof(struct pwm_sifive_priv),