blob: ffc37180eb42bb72627bcca73cbf1fb3612355f7 [file] [log] [blame]
Dan Sneddon1f302a92021-09-20 16:28:44 -07001// SPDX-License-Identifier: GPL-2.0+
2/*
3 * PWM support for Microchip AT91 architectures.
4 *
5 * Copyright (C) 2021 Microchip Technology Inc. and its subsidiaries
6 *
7 * Author: Dan Sneddon <daniel.sneddon@microchip.com>
8 *
9 * Based on drivers/pwm/pwm-atmel.c from Linux.
10 */
11#include <clk.h>
Dan Sneddon1f302a92021-09-20 16:28:44 -070012#include <div64.h>
13#include <dm.h>
14#include <linux/bitops.h>
15#include <linux/io.h>
Igor Prusovc3421ea2023-11-09 20:10:04 +030016#include <linux/time.h>
Dan Sneddon1f302a92021-09-20 16:28:44 -070017#include <pwm.h>
18
19#define PERIOD_BITS 16
20#define PWM_MAX_PRES 10
Dan Sneddon1f302a92021-09-20 16:28:44 -070021
22#define PWM_ENA 0x04
23#define PWM_CHANNEL_OFFSET 0x20
24#define PWM_CMR 0x200
25#define PWM_CMR_CPRE_MSK GENMASK(3, 0)
26#define PWM_CMR_CPOL BIT(9)
27#define PWM_CDTY 0x204
28#define PWM_CPRD 0x20C
29
30struct at91_pwm_priv {
31 void __iomem *base;
32 struct clk pclk;
33 u32 clkrate;
34};
35
36static int at91_pwm_calculate_cprd_and_pres(struct udevice *dev,
37 unsigned long clkrate,
38 uint period_ns, uint duty_ns,
39 unsigned long *cprd, u32 *pres)
40{
41 u64 cycles = period_ns;
42 int shift;
43
44 /* Calculate the period cycles and prescale value */
45 cycles *= clkrate;
46 do_div(cycles, NSEC_PER_SEC);
47
48 /*
49 * The register for the period length is period_bits bits wide.
50 * So for each bit the number of clock cycles is wider divide the input
51 * clock frequency by two using pres and shift cprd accordingly.
52 */
53 shift = fls(cycles) - PERIOD_BITS;
54
55 if (shift > PWM_MAX_PRES) {
56 return -EINVAL;
57 } else if (shift > 0) {
58 *pres = shift;
59 cycles >>= *pres;
60 } else {
61 *pres = 0;
62 }
63
64 *cprd = cycles;
65
66 return 0;
67}
68
69static void at91_pwm_calculate_cdty(uint period_ns, uint duty_ns,
70 unsigned long clkrate, unsigned long cprd,
71 u32 pres, unsigned long *cdty)
72{
73 u64 cycles = duty_ns;
74
75 cycles *= clkrate;
76 do_div(cycles, NSEC_PER_SEC);
77 cycles >>= pres;
78 *cdty = cprd - cycles;
79}
80
81/**
82 * Returns: channel status after set operation
83 */
84static bool at91_pwm_set(void __iomem *base, uint channel, bool enable)
85{
86 u32 val, cur_status;
87
88 val = ioread32(base + PWM_ENA);
89 cur_status = !!(val & BIT(channel));
90
91 /* if channel is already in that state, do nothing */
92 if (!(enable ^ cur_status))
93 return cur_status;
94
95 if (enable)
96 val |= BIT(channel);
97 else
98 val &= ~(BIT(channel));
99
100 iowrite32(val, base + PWM_ENA);
101
102 return cur_status;
103}
104
105static int at91_pwm_set_enable(struct udevice *dev, uint channel, bool enable)
106{
107 struct at91_pwm_priv *priv = dev_get_priv(dev);
108
109 at91_pwm_set(priv->base, channel, enable);
110
111 return 0;
112}
113
114static int at91_pwm_set_config(struct udevice *dev, uint channel,
115 uint period_ns, uint duty_ns)
116{
117 struct at91_pwm_priv *priv = dev_get_priv(dev);
118 unsigned long cprd, cdty;
119 u32 pres, val;
120 int channel_enabled;
121 int ret;
122
123 ret = at91_pwm_calculate_cprd_and_pres(dev, priv->clkrate, period_ns,
124 duty_ns, &cprd, &pres);
125 if (ret)
126 return ret;
127
128 at91_pwm_calculate_cdty(period_ns, duty_ns, priv->clkrate, cprd, pres, &cdty);
129
130 /* disable the channel */
131 channel_enabled = at91_pwm_set(priv->base, channel, false);
132
133 /* It is necessary to preserve CPOL, inside CMR */
134 val = ioread32(priv->base + (channel * PWM_CHANNEL_OFFSET) + PWM_CMR);
135 val = (val & ~PWM_CMR_CPRE_MSK) | (pres & PWM_CMR_CPRE_MSK);
136 iowrite32(val, priv->base + (channel * PWM_CHANNEL_OFFSET) + PWM_CMR);
137
138 iowrite32(cprd, priv->base + (channel * PWM_CHANNEL_OFFSET) + PWM_CPRD);
139
140 iowrite32(cdty, priv->base + (channel * PWM_CHANNEL_OFFSET) + PWM_CDTY);
141
142 /* renable the channel if needed */
143 if (channel_enabled)
144 at91_pwm_set(priv->base, channel, true);
145
146 return 0;
147}
148
149static int at91_pwm_set_invert(struct udevice *dev, uint channel,
150 bool polarity)
151{
152 struct at91_pwm_priv *priv = dev_get_priv(dev);
153 u32 val;
154
155 val = ioread32(priv->base + (channel * PWM_CHANNEL_OFFSET) + PWM_CMR);
156 if (polarity)
157 val |= PWM_CMR_CPOL;
158 else
159 val &= ~PWM_CMR_CPOL;
160 iowrite32(val, priv->base + (channel * PWM_CHANNEL_OFFSET) + PWM_CMR);
161
162 return 0;
163}
164
165static int at91_pwm_probe(struct udevice *dev)
166{
167 struct at91_pwm_priv *priv = dev_get_priv(dev);
168 int ret;
169
170 priv->base = dev_read_addr_ptr(dev);
171 if (!priv->base)
172 return -EINVAL;
173
174 ret = clk_get_by_index(dev, 0, &priv->pclk);
175 if (ret)
176 return ret;
177
178 /* clocks aren't ref-counted so just enabled them once here */
179 ret = clk_enable(&priv->pclk);
180 if (ret)
181 return ret;
182
183 priv->clkrate = clk_get_rate(&priv->pclk);
184
185 return ret;
186}
187
188static const struct pwm_ops at91_pwm_ops = {
189 .set_config = at91_pwm_set_config,
190 .set_enable = at91_pwm_set_enable,
191 .set_invert = at91_pwm_set_invert,
192};
193
194static const struct udevice_id at91_pwm_of_match[] = {
195 { .compatible = "atmel,sama5d2-pwm" },
196 { }
197};
198
199U_BOOT_DRIVER(at91_pwm) = {
200 .name = "at91_pwm",
201 .id = UCLASS_PWM,
202 .of_match = at91_pwm_of_match,
203 .probe = at91_pwm_probe,
204 .priv_auto = sizeof(struct at91_pwm_priv),
205 .ops = &at91_pwm_ops,
206};