blob: 898e353b37031eb1c8ea2b01405d6c13f5be9deb [file] [log] [blame]
developerdcb02062020-02-21 21:01:46 +08001// 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
developerdcb02062020-02-21 21:01:46 +08008#include <clk.h>
9#include <dm.h>
10#include <pwm.h>
11#include <div64.h>
12#include <linux/bitops.h>
13#include <linux/io.h>
Igor Prusovc3421ea2023-11-09 20:10:04 +030014#include <linux/time.h>
developerdcb02062020-02-21 21:01:46 +080015
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
developer910cce72022-09-09 19:59:38 +080030enum mtk_pwm_reg_ver {
31 PWM_REG_V1,
32 PWM_REG_V2,
developer5551adc2025-03-07 11:22:23 +080033 PWM_REG_V3,
developer910cce72022-09-09 19:59:38 +080034};
35
36static const unsigned int mtk_pwm_reg_offset_v1[] = {
developerdcb02062020-02-21 21:01:46 +080037 0x0010, 0x0050, 0x0090, 0x00d0, 0x0110, 0x0150, 0x0190, 0x0220
38};
39
developer910cce72022-09-09 19:59:38 +080040static const unsigned int mtk_pwm_reg_offset_v2[] = {
41 0x0080, 0x00c0, 0x0100, 0x0140, 0x0180, 0x01c0, 0x0200, 0x0240
42};
43
developer5551adc2025-03-07 11:22:23 +080044static const unsigned int mtk_pwm_reg_offset_v3[] = {
45 0x0100, 0x0200, 0x0300, 0x0400, 0x0500, 0x600, 0x700, 0x0800
46};
47
developerdcb02062020-02-21 21:01:46 +080048struct mtk_pwm_soc {
49 unsigned int num_pwms;
50 bool pwm45_fixup;
developer910cce72022-09-09 19:59:38 +080051 enum mtk_pwm_reg_ver reg_ver;
developerdcb02062020-02-21 21:01:46 +080052};
53
54struct mtk_pwm_priv {
55 void __iomem *base;
56 struct clk top_clk;
57 struct clk main_clk;
58 struct clk pwm_clks[MAX_PWM_NUM];
59 const struct mtk_pwm_soc *soc;
60};
61
62static void mtk_pwm_w32(struct udevice *dev, uint channel, uint reg, uint val)
63{
64 struct mtk_pwm_priv *priv = dev_get_priv(dev);
developer910cce72022-09-09 19:59:38 +080065 u32 offset;
66
67 switch (priv->soc->reg_ver) {
developer5551adc2025-03-07 11:22:23 +080068 case PWM_REG_V3:
69 offset = mtk_pwm_reg_offset_v3[channel];
70 break;
71
developer910cce72022-09-09 19:59:38 +080072 case PWM_REG_V2:
73 offset = mtk_pwm_reg_offset_v2[channel];
74 break;
75
76 default:
77 offset = mtk_pwm_reg_offset_v1[channel];
78 }
developerdcb02062020-02-21 21:01:46 +080079
80 writel(val, priv->base + offset + reg);
81}
82
83static int mtk_pwm_set_config(struct udevice *dev, uint channel,
84 uint period_ns, uint duty_ns)
85{
86 struct mtk_pwm_priv *priv = dev_get_priv(dev);
87 u32 clkdiv = 0, clksel = 0, cnt_period, cnt_duty,
88 reg_width = PWMDWIDTH, reg_thres = PWMTHRES;
89 u64 resolution;
90 int ret = 0;
91
92 clk_enable(&priv->top_clk);
93 clk_enable(&priv->main_clk);
94 /* Using resolution in picosecond gets accuracy higher */
95 resolution = (u64)NSEC_PER_SEC * 1000;
96 do_div(resolution, clk_get_rate(&priv->pwm_clks[channel]));
97 cnt_period = DIV_ROUND_CLOSEST_ULL((u64)period_ns * 1000, resolution);
98 while (cnt_period > 8191) {
99 resolution *= 2;
100 clkdiv++;
101 cnt_period = DIV_ROUND_CLOSEST_ULL((u64)period_ns * 1000,
102 resolution);
103 if (clkdiv > PWM_CLK_DIV_MAX && clksel == 0) {
104 clksel = 1;
105 clkdiv = 0;
106 resolution = (u64)NSEC_PER_SEC * 1000 * 1625;
107 do_div(resolution,
108 clk_get_rate(&priv->pwm_clks[channel]));
109 cnt_period = DIV_ROUND_CLOSEST_ULL(
110 (u64)period_ns * 1000, resolution);
111 clk_enable(&priv->pwm_clks[channel]);
112 }
113 }
114 if (clkdiv > PWM_CLK_DIV_MAX && clksel == 1) {
115 printf("pwm period %u not supported\n", period_ns);
116 return -EINVAL;
117 }
118 if (priv->soc->pwm45_fixup && channel > 2) {
119 /*
120 * PWM[4,5] has distinct offset for PWMDWIDTH and PWMTHRES
121 * from the other PWMs on MT7623.
122 */
123 reg_width = PWM45DWIDTH_FIXUP;
124 reg_thres = PWM45THRES_FIXUP;
125 }
126 cnt_duty = DIV_ROUND_CLOSEST_ULL((u64)duty_ns * 1000, resolution);
127 if (clksel == 1)
128 mtk_pwm_w32(dev, channel, PWMCON, BIT(15) | BIT(3) | clkdiv);
129 else
130 mtk_pwm_w32(dev, channel, PWMCON, BIT(15) | clkdiv);
131 mtk_pwm_w32(dev, channel, reg_width, cnt_period);
132 mtk_pwm_w32(dev, channel, reg_thres, cnt_duty);
133
134 return ret;
135};
136
137static int mtk_pwm_set_enable(struct udevice *dev, uint channel, bool enable)
138{
139 struct mtk_pwm_priv *priv = dev_get_priv(dev);
140 u32 val = 0;
141
142 val = readl(priv->base);
143 if (enable)
144 val |= BIT(channel);
145 else
146 val &= ~BIT(channel);
147 writel(val, priv->base);
148
149 return 0;
150};
151
152static int mtk_pwm_probe(struct udevice *dev)
153{
154 struct mtk_pwm_priv *priv = dev_get_priv(dev);
155 int ret = 0;
156 int i;
157
158 priv->soc = (struct mtk_pwm_soc *)dev_get_driver_data(dev);
Masahiro Yamada1096ae12020-07-17 14:36:46 +0900159 priv->base = dev_read_addr_ptr(dev);
developerdcb02062020-02-21 21:01:46 +0800160 if (!priv->base)
161 return -EINVAL;
162 ret = clk_get_by_name(dev, "top", &priv->top_clk);
163 if (ret < 0)
164 return ret;
165 ret = clk_get_by_name(dev, "main", &priv->main_clk);
166 if (ret < 0)
167 return ret;
168 for (i = 0; i < priv->soc->num_pwms; i++) {
169 char name[8];
170
171 snprintf(name, sizeof(name), "pwm%d", i + 1);
172 ret = clk_get_by_name(dev, name, &priv->pwm_clks[i]);
173 if (ret < 0)
174 return ret;
175 }
176
177 return ret;
178}
179
180static const struct pwm_ops mtk_pwm_ops = {
181 .set_config = mtk_pwm_set_config,
182 .set_enable = mtk_pwm_set_enable,
183};
184
185static const struct mtk_pwm_soc mt7622_data = {
186 .num_pwms = 6,
187 .pwm45_fixup = false,
developer910cce72022-09-09 19:59:38 +0800188 .reg_ver = PWM_REG_V1,
developerdcb02062020-02-21 21:01:46 +0800189};
190
191static const struct mtk_pwm_soc mt7623_data = {
192 .num_pwms = 5,
193 .pwm45_fixup = true,
developer910cce72022-09-09 19:59:38 +0800194 .reg_ver = PWM_REG_V1,
developerdcb02062020-02-21 21:01:46 +0800195};
196
197static const struct mtk_pwm_soc mt7629_data = {
198 .num_pwms = 1,
199 .pwm45_fixup = false,
developer910cce72022-09-09 19:59:38 +0800200 .reg_ver = PWM_REG_V1,
201};
202
203static const struct mtk_pwm_soc mt7981_data = {
developerd24fd7e2025-01-17 17:18:06 +0800204 .num_pwms = 3,
developer910cce72022-09-09 19:59:38 +0800205 .pwm45_fixup = false,
206 .reg_ver = PWM_REG_V2,
developerdcb02062020-02-21 21:01:46 +0800207};
208
developer38f75332022-09-09 19:59:36 +0800209static const struct mtk_pwm_soc mt7986_data = {
210 .num_pwms = 2,
211 .pwm45_fixup = false,
developer910cce72022-09-09 19:59:38 +0800212 .reg_ver = PWM_REG_V1,
developer38f75332022-09-09 19:59:36 +0800213};
214
developer5551adc2025-03-07 11:22:23 +0800215static const struct mtk_pwm_soc mt7987_data = {
216 .num_pwms = 3,
217 .pwm45_fixup = false,
218 .reg_ver = PWM_REG_V3,
219};
220
developer356d5e22023-07-19 17:16:24 +0800221static const struct mtk_pwm_soc mt7988_data = {
222 .num_pwms = 8,
223 .pwm45_fixup = false,
224 .reg_ver = PWM_REG_V2,
225};
226
developerdcb02062020-02-21 21:01:46 +0800227static const struct udevice_id mtk_pwm_ids[] = {
228 { .compatible = "mediatek,mt7622-pwm", .data = (ulong)&mt7622_data },
229 { .compatible = "mediatek,mt7623-pwm", .data = (ulong)&mt7623_data },
230 { .compatible = "mediatek,mt7629-pwm", .data = (ulong)&mt7629_data },
developer910cce72022-09-09 19:59:38 +0800231 { .compatible = "mediatek,mt7981-pwm", .data = (ulong)&mt7981_data },
developer38f75332022-09-09 19:59:36 +0800232 { .compatible = "mediatek,mt7986-pwm", .data = (ulong)&mt7986_data },
developer5551adc2025-03-07 11:22:23 +0800233 { .compatible = "mediatek,mt7987-pwm", .data = (ulong)&mt7987_data },
developer356d5e22023-07-19 17:16:24 +0800234 { .compatible = "mediatek,mt7988-pwm", .data = (ulong)&mt7988_data },
developerdcb02062020-02-21 21:01:46 +0800235 { }
236};
237
238U_BOOT_DRIVER(mtk_pwm) = {
239 .name = "mtk_pwm",
240 .id = UCLASS_PWM,
241 .of_match = mtk_pwm_ids,
242 .ops = &mtk_pwm_ops,
243 .probe = mtk_pwm_probe,
Simon Glass8a2b47f2020-12-03 16:55:17 -0700244 .priv_auto = sizeof(struct mtk_pwm_priv),
developerdcb02062020-02-21 21:01:46 +0800245};