phy: atheros: move delay config to common function
[oweals/u-boot.git] / drivers / pwm / pwm-mtk.c
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
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
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
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
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 = (void __iomem *)devfdt_get_addr(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_alloc_size = sizeof(struct mtk_pwm_priv),
188 };