1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * Copyright (C) 2020 MediaTek Inc. All Rights Reserved.
4  *
5  * Author: Sam Shih <sam.shih@mediatek.com>
6  */
7 
8 #include <common.h>
9 #include <clk.h>
10 #include <dm.h>
11 #include <pwm.h>
12 #include <div64.h>
13 #include <linux/bitops.h>
14 #include <linux/io.h>
15 
16 /* PWM registers and bits definitions */
17 #define PWMCON			0x00
18 #define PWMHDUR			0x04
19 #define PWMLDUR			0x08
20 #define PWMGDUR			0x0c
21 #define PWMWAVENUM		0x28
22 #define PWMDWIDTH		0x2c
23 #define PWM45DWIDTH_FIXUP	0x30
24 #define PWMTHRES		0x30
25 #define PWM45THRES_FIXUP	0x34
26 
27 #define PWM_CLK_DIV_MAX		7
28 #define MAX_PWM_NUM		8
29 
30 #define NSEC_PER_SEC 1000000000L
31 
32 static const unsigned int mtk_pwm_reg_offset[] = {
33 	0x0010, 0x0050, 0x0090, 0x00d0, 0x0110, 0x0150, 0x0190, 0x0220
34 };
35 
36 struct mtk_pwm_soc {
37 	unsigned int num_pwms;
38 	bool pwm45_fixup;
39 };
40 
41 struct mtk_pwm_priv {
42 	void __iomem *base;
43 	struct clk top_clk;
44 	struct clk main_clk;
45 	struct clk pwm_clks[MAX_PWM_NUM];
46 	const struct mtk_pwm_soc *soc;
47 };
48 
mtk_pwm_w32(struct udevice * dev,uint channel,uint reg,uint val)49 static void mtk_pwm_w32(struct udevice *dev, uint channel, uint reg, uint val)
50 {
51 	struct mtk_pwm_priv *priv = dev_get_priv(dev);
52 	u32 offset = mtk_pwm_reg_offset[channel];
53 
54 	writel(val, priv->base + offset + reg);
55 }
56 
mtk_pwm_set_config(struct udevice * dev,uint channel,uint period_ns,uint duty_ns)57 static int mtk_pwm_set_config(struct udevice *dev, uint channel,
58 			      uint period_ns, uint duty_ns)
59 {
60 	struct mtk_pwm_priv *priv = dev_get_priv(dev);
61 	u32 clkdiv = 0, clksel = 0, cnt_period, cnt_duty,
62 	    reg_width = PWMDWIDTH, reg_thres = PWMTHRES;
63 	u64 resolution;
64 	int ret = 0;
65 
66 	clk_enable(&priv->top_clk);
67 	clk_enable(&priv->main_clk);
68 	/* Using resolution in picosecond gets accuracy higher */
69 	resolution = (u64)NSEC_PER_SEC * 1000;
70 	do_div(resolution, clk_get_rate(&priv->pwm_clks[channel]));
71 	cnt_period = DIV_ROUND_CLOSEST_ULL((u64)period_ns * 1000, resolution);
72 	while (cnt_period > 8191) {
73 		resolution *= 2;
74 		clkdiv++;
75 		cnt_period = DIV_ROUND_CLOSEST_ULL((u64)period_ns * 1000,
76 						   resolution);
77 		if (clkdiv > PWM_CLK_DIV_MAX && clksel == 0) {
78 			clksel = 1;
79 			clkdiv = 0;
80 			resolution = (u64)NSEC_PER_SEC * 1000 * 1625;
81 			do_div(resolution,
82 			       clk_get_rate(&priv->pwm_clks[channel]));
83 			cnt_period = DIV_ROUND_CLOSEST_ULL(
84 					(u64)period_ns * 1000, resolution);
85 			clk_enable(&priv->pwm_clks[channel]);
86 		}
87 	}
88 	if (clkdiv > PWM_CLK_DIV_MAX && clksel == 1) {
89 		printf("pwm period %u not supported\n", period_ns);
90 		return -EINVAL;
91 	}
92 	if (priv->soc->pwm45_fixup && channel > 2) {
93 		/*
94 		 * PWM[4,5] has distinct offset for PWMDWIDTH and PWMTHRES
95 		 * from the other PWMs on MT7623.
96 		 */
97 		reg_width = PWM45DWIDTH_FIXUP;
98 		reg_thres = PWM45THRES_FIXUP;
99 	}
100 	cnt_duty = DIV_ROUND_CLOSEST_ULL((u64)duty_ns * 1000, resolution);
101 	if (clksel == 1)
102 		mtk_pwm_w32(dev, channel, PWMCON, BIT(15) | BIT(3) | clkdiv);
103 	else
104 		mtk_pwm_w32(dev, channel, PWMCON, BIT(15) | clkdiv);
105 	mtk_pwm_w32(dev, channel, reg_width, cnt_period);
106 	mtk_pwm_w32(dev, channel, reg_thres, cnt_duty);
107 
108 	return ret;
109 };
110 
mtk_pwm_set_enable(struct udevice * dev,uint channel,bool enable)111 static int mtk_pwm_set_enable(struct udevice *dev, uint channel, bool enable)
112 {
113 	struct mtk_pwm_priv *priv = dev_get_priv(dev);
114 	u32 val = 0;
115 
116 	val = readl(priv->base);
117 	if (enable)
118 		val |= BIT(channel);
119 	else
120 		val &= ~BIT(channel);
121 	writel(val, priv->base);
122 
123 	return 0;
124 };
125 
mtk_pwm_probe(struct udevice * dev)126 static int mtk_pwm_probe(struct udevice *dev)
127 {
128 	struct mtk_pwm_priv *priv = dev_get_priv(dev);
129 	int ret = 0;
130 	int i;
131 
132 	priv->soc = (struct mtk_pwm_soc *)dev_get_driver_data(dev);
133 	priv->base = dev_read_addr_ptr(dev);
134 	if (!priv->base)
135 		return -EINVAL;
136 	ret = clk_get_by_name(dev, "top", &priv->top_clk);
137 	if (ret < 0)
138 		return ret;
139 	ret = clk_get_by_name(dev, "main", &priv->main_clk);
140 	if (ret < 0)
141 		return ret;
142 	for (i = 0; i < priv->soc->num_pwms; i++) {
143 		char name[8];
144 
145 		snprintf(name, sizeof(name), "pwm%d", i + 1);
146 		ret = clk_get_by_name(dev, name, &priv->pwm_clks[i]);
147 		if (ret < 0)
148 			return ret;
149 	}
150 
151 	return ret;
152 }
153 
154 static const struct pwm_ops mtk_pwm_ops = {
155 	.set_config	= mtk_pwm_set_config,
156 	.set_enable	= mtk_pwm_set_enable,
157 };
158 
159 static const struct mtk_pwm_soc mt7622_data = {
160 	.num_pwms = 6,
161 	.pwm45_fixup = false,
162 };
163 
164 static const struct mtk_pwm_soc mt7623_data = {
165 	.num_pwms = 5,
166 	.pwm45_fixup = true,
167 };
168 
169 static const struct mtk_pwm_soc mt7629_data = {
170 	.num_pwms = 1,
171 	.pwm45_fixup = false,
172 };
173 
174 static const struct udevice_id mtk_pwm_ids[] = {
175 	{ .compatible = "mediatek,mt7622-pwm", .data = (ulong)&mt7622_data },
176 	{ .compatible = "mediatek,mt7623-pwm", .data = (ulong)&mt7623_data },
177 	{ .compatible = "mediatek,mt7629-pwm", .data = (ulong)&mt7629_data },
178 	{ }
179 };
180 
181 U_BOOT_DRIVER(mtk_pwm) = {
182 	.name = "mtk_pwm",
183 	.id = UCLASS_PWM,
184 	.of_match = mtk_pwm_ids,
185 	.ops = &mtk_pwm_ops,
186 	.probe = mtk_pwm_probe,
187 	.priv_auto	= sizeof(struct mtk_pwm_priv),
188 };
189