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 
41 #define PWM_SIFIVE_CHANNEL_ENABLE_VAL   0
42 #define PWM_SIFIVE_CHANNEL_DISABLE_VAL  0xffff
43 
44 DECLARE_GLOBAL_DATA_PTR;
45 
46 struct pwm_sifive_regs {
47 	unsigned long cfg;
48 	unsigned long cnt;
49 	unsigned long pwms;
50 	unsigned long cmp0;
51 };
52 
53 struct pwm_sifive_data {
54 	struct pwm_sifive_regs regs;
55 };
56 
57 struct pwm_sifive_priv {
58 	void __iomem *base;
59 	ulong freq;
60 	const struct pwm_sifive_data *data;
61 };
62 
pwm_sifive_set_config(struct udevice * dev,uint channel,uint period_ns,uint duty_ns)63 static int pwm_sifive_set_config(struct udevice *dev, uint channel,
64 				 uint period_ns, uint duty_ns)
65 {
66 	struct pwm_sifive_priv *priv = dev_get_priv(dev);
67 	const struct pwm_sifive_regs *regs = &priv->data->regs;
68 	unsigned long scale_pow;
69 	unsigned long long num;
70 	u32 scale, val = 0, frac;
71 
72 	debug("%s: period_ns=%u, duty_ns=%u\n", __func__, period_ns, duty_ns);
73 
74 	/*
75 	 * The PWM unit is used with pwmzerocmp=0, so the only way to modify the
76 	 * period length is using pwmscale which provides the number of bits the
77 	 * counter is shifted before being feed to the comparators. A period
78 	 * lasts (1 << (PWM_SIFIVE_CMPWIDTH + pwmscale)) clock ticks.
79 	 * (1 << (PWM_SIFIVE_CMPWIDTH + scale)) * 10^9/rate = period
80 	 */
81 	scale_pow = lldiv((uint64_t)priv->freq * period_ns, 1000000000);
82 	scale = clamp(ilog2(scale_pow) - PWM_SIFIVE_CMPWIDTH, 0, 0xf);
83 	val |= (FIELD_PREP(PWM_SIFIVE_PWMCFG_SCALE, scale) | PWM_SIFIVE_PWMCFG_EN_ALWAYS);
84 
85 	/*
86 	 * The problem of output producing mixed setting as mentioned at top,
87 	 * occurs here. To minimize the window for this problem, we are
88 	 * calculating the register values first and then writing them
89 	 * consecutively
90 	 */
91 	num = (u64)duty_ns * (1U << PWM_SIFIVE_CMPWIDTH);
92 	frac = DIV_ROUND_CLOSEST_ULL(num, period_ns);
93 	frac = min(frac, (1U << PWM_SIFIVE_CMPWIDTH) - 1);
94 	frac = (1U << PWM_SIFIVE_CMPWIDTH) - 1 - frac;
95 
96 	writel(val, priv->base + regs->cfg);
97 	writel(frac, priv->base + regs->cmp0 + channel *
98 	       PWM_SIFIVE_SIZE_PWMCMP);
99 
100 	return 0;
101 }
102 
pwm_sifive_set_enable(struct udevice * dev,uint channel,bool enable)103 static int pwm_sifive_set_enable(struct udevice *dev, uint channel, bool enable)
104 {
105 	struct pwm_sifive_priv *priv = dev_get_priv(dev);
106 	const struct pwm_sifive_regs *regs = &priv->data->regs;
107 
108 	debug("%s: Enable '%s'\n", __func__, dev->name);
109 
110 	if (enable)
111 		writel(PWM_SIFIVE_CHANNEL_ENABLE_VAL, priv->base +
112 		       regs->cmp0 + channel * PWM_SIFIVE_SIZE_PWMCMP);
113 	else
114 		writel(PWM_SIFIVE_CHANNEL_DISABLE_VAL, priv->base +
115 		       regs->cmp0 + channel * PWM_SIFIVE_SIZE_PWMCMP);
116 
117 	return 0;
118 }
119 
pwm_sifive_of_to_plat(struct udevice * dev)120 static int pwm_sifive_of_to_plat(struct udevice *dev)
121 {
122 	struct pwm_sifive_priv *priv = dev_get_priv(dev);
123 
124 	priv->base = dev_read_addr_ptr(dev);
125 
126 	return 0;
127 }
128 
pwm_sifive_probe(struct udevice * dev)129 static int pwm_sifive_probe(struct udevice *dev)
130 {
131 	struct pwm_sifive_priv *priv = dev_get_priv(dev);
132 	struct clk clk;
133 	int ret = 0;
134 
135 	ret = clk_get_by_index(dev, 0, &clk);
136 	if (ret < 0) {
137 		debug("%s get clock fail!\n", __func__);
138 		return -EINVAL;
139 	}
140 
141 	priv->freq = clk_get_rate(&clk);
142 	priv->data = (struct pwm_sifive_data *)dev_get_driver_data(dev);
143 
144 	return 0;
145 }
146 
147 static const struct pwm_ops pwm_sifive_ops = {
148 	.set_config	= pwm_sifive_set_config,
149 	.set_enable	= pwm_sifive_set_enable,
150 };
151 
152 static const struct pwm_sifive_data pwm_data = {
153 	.regs = {
154 		.cfg = 0x00,
155 		.cnt = 0x08,
156 		.pwms = 0x10,
157 		.cmp0 = 0x20,
158 	},
159 };
160 
161 static const struct udevice_id pwm_sifive_ids[] = {
162 	{ .compatible = "sifive,pwm0", .data = (ulong)&pwm_data},
163 	{ }
164 };
165 
166 U_BOOT_DRIVER(pwm_sifive) = {
167 	.name	= "pwm_sifive",
168 	.id	= UCLASS_PWM,
169 	.of_match = pwm_sifive_ids,
170 	.ops	= &pwm_sifive_ops,
171 	.of_to_plat     = pwm_sifive_of_to_plat,
172 	.probe		= pwm_sifive_probe,
173 	.priv_auto	= sizeof(struct pwm_sifive_priv),
174 };
175