Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/sys/arm/ti/clk/ti_divider_clock.c
39536 views
1
/*-
2
* SPDX-License-Identifier: BSD-2-Clause
3
*
4
* Copyright (c) 2020 Oskar Holmlund <[email protected]>
5
*
6
* Redistribution and use in source and binary forms, with or without
7
* modification, are permitted provided that the following conditions
8
* are met:
9
* 1. Redistributions of source code must retain the above copyright
10
* notice, this list of conditions and the following disclaimer.
11
* 2. Redistributions in binary form must reproduce the above copyright
12
* notice, this list of conditions and the following disclaimer in the
13
* documentation and/or other materials provided with the distribution.
14
*
15
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
16
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
19
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
20
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
21
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
22
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
23
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25
* SUCH DAMAGE.
26
*/
27
28
#include <sys/param.h>
29
#include <sys/conf.h>
30
#include <sys/bus.h>
31
#include <sys/kernel.h>
32
#include <sys/module.h>
33
#include <sys/systm.h>
34
#include <sys/libkern.h>
35
36
#include <machine/bus.h>
37
#include <dev/fdt/simplebus.h>
38
39
#include <dev/clk/clk_div.h>
40
#include <dev/ofw/ofw_bus.h>
41
#include <dev/ofw/ofw_bus_subr.h>
42
43
#include "clock_common.h"
44
45
#if 0
46
#define DPRINTF(dev, msg...) device_printf(dev, msg)
47
#else
48
#define DPRINTF(dev, msg...)
49
#endif
50
51
/*
52
* Devicetree description
53
* Documentation/devicetree/bindings/clock/ti/divider.txt
54
*/
55
56
struct ti_divider_softc {
57
device_t sc_dev;
58
bool attach_done;
59
struct clk_div_def div_def;
60
61
struct clock_cell_info clock_cell;
62
struct clkdom *clkdom;
63
};
64
65
static int ti_divider_probe(device_t dev);
66
static int ti_divider_attach(device_t dev);
67
static int ti_divider_detach(device_t dev);
68
69
#define TI_DIVIDER_CLOCK 2
70
#define TI_COMPOSITE_DIVIDER_CLOCK 1
71
#define TI_DIVIDER_END 0
72
73
static struct ofw_compat_data compat_data[] = {
74
{ "ti,divider-clock", TI_DIVIDER_CLOCK },
75
{ "ti,composite-divider-clock", TI_COMPOSITE_DIVIDER_CLOCK },
76
{ NULL, TI_DIVIDER_END }
77
};
78
79
static int
80
register_clk(struct ti_divider_softc *sc) {
81
int err;
82
83
sc->clkdom = clkdom_create(sc->sc_dev);
84
if (sc->clkdom == NULL) {
85
DPRINTF(sc->sc_dev, "Failed to create clkdom\n");
86
return (ENXIO);
87
}
88
89
err = clknode_div_register(sc->clkdom, &sc->div_def);
90
if (err) {
91
DPRINTF(sc->sc_dev, "clknode_div_register failed %x\n", err);
92
return (ENXIO);
93
}
94
95
err = clkdom_finit(sc->clkdom);
96
if (err) {
97
DPRINTF(sc->sc_dev, "Clk domain finit fails %x.\n", err);
98
return (ENXIO);
99
}
100
101
return (0);
102
}
103
104
static int
105
ti_divider_probe(device_t dev)
106
{
107
if (!ofw_bus_status_okay(dev))
108
return (ENXIO);
109
110
if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0)
111
return (ENXIO);
112
113
device_set_desc(dev, "TI Divider Clock");
114
115
return (BUS_PROBE_DEFAULT);
116
}
117
118
static int
119
ti_divider_attach(device_t dev)
120
{
121
struct ti_divider_softc *sc;
122
phandle_t node;
123
int err;
124
cell_t value;
125
uint32_t ti_max_div;
126
127
sc = device_get_softc(dev);
128
sc->sc_dev = dev;
129
node = ofw_bus_get_node(dev);
130
131
/* Grab the content of reg properties */
132
OF_getencprop(node, "reg", &value, sizeof(value));
133
sc->div_def.offset = value;
134
135
if (OF_hasprop(node, "ti,bit-shift")) {
136
OF_getencprop(node, "ti,bit-shift", &value, sizeof(value));
137
sc->div_def.i_shift = value;
138
}
139
140
if (OF_hasprop(node, "ti,index-starts-at-one")) {
141
sc->div_def.div_flags = CLK_DIV_ZERO_BASED;
142
}
143
144
if (OF_hasprop(node, "ti,index-power-of-two")) {
145
/* FIXME: later */
146
device_printf(sc->sc_dev, "ti,index-power-of-two - Not implemented\n");
147
/* remember to update i_width a few lines below */
148
}
149
if (OF_hasprop(node, "ti,max-div")) {
150
OF_getencprop(node, "ti,max-div", &value, sizeof(value));
151
ti_max_div = value;
152
}
153
154
if (OF_hasprop(node, "clock-output-names"))
155
device_printf(sc->sc_dev, "clock-output-names\n");
156
if (OF_hasprop(node, "ti,dividers"))
157
device_printf(sc->sc_dev, "ti,dividers\n");
158
if (OF_hasprop(node, "ti,min-div"))
159
device_printf(sc->sc_dev, "ti,min-div - Not implemented\n");
160
161
if (OF_hasprop(node, "ti,autoidle-shift"))
162
device_printf(sc->sc_dev, "ti,autoidle-shift - Not implemented\n");
163
if (OF_hasprop(node, "ti,set-rate-parent"))
164
device_printf(sc->sc_dev, "ti,set-rate-parent - Not implemented\n");
165
if (OF_hasprop(node, "ti,latch-bit"))
166
device_printf(sc->sc_dev, "ti,latch-bit - Not implemented\n");
167
168
/* Figure out the width from ti_max_div */
169
if (sc->div_def.div_flags)
170
sc->div_def.i_width = fls(ti_max_div-1);
171
else
172
sc->div_def.i_width = fls(ti_max_div);
173
174
DPRINTF(sc->sc_dev, "div_def.i_width %x\n", sc->div_def.i_width);
175
176
read_clock_cells(sc->sc_dev, &sc->clock_cell);
177
178
create_clkdef(sc->sc_dev, &sc->clock_cell, &sc->div_def.clkdef);
179
180
err = find_parent_clock_names(sc->sc_dev, &sc->clock_cell, &sc->div_def.clkdef);
181
182
if (err) {
183
/* free_clkdef will be called in ti_divider_new_pass */
184
DPRINTF(sc->sc_dev, "find_parent_clock_names failed\n");
185
bus_attach_children(sc->sc_dev);
186
return (0);
187
}
188
189
err = register_clk(sc);
190
191
if (err) {
192
/* free_clkdef will be called in ti_divider_new_pass */
193
DPRINTF(sc->sc_dev, "register_clk failed\n");
194
bus_attach_children(sc->sc_dev);
195
return (0);
196
}
197
198
sc->attach_done = true;
199
200
free_clkdef(&sc->div_def.clkdef);
201
202
bus_attach_children(sc->sc_dev);
203
return (0);
204
}
205
206
static int
207
ti_divider_detach(device_t dev)
208
{
209
return (EBUSY);
210
}
211
212
static void
213
ti_divider_new_pass(device_t dev)
214
{
215
struct ti_divider_softc *sc;
216
int err;
217
218
sc = device_get_softc(dev);
219
220
if (sc->attach_done) {
221
return;
222
}
223
224
err = find_parent_clock_names(sc->sc_dev, &sc->clock_cell, &sc->div_def.clkdef);
225
if (err) {
226
/* free_clkdef will be called in a later call to ti_divider_new_pass */
227
DPRINTF(sc->sc_dev, "new_pass find_parent_clock_names failed\n");
228
return;
229
}
230
231
err = register_clk(sc);
232
if (err) {
233
/* free_clkdef will be called in a later call to ti_divider_new_pass */
234
DPRINTF(sc->sc_dev, "new_pass register_clk failed\n");
235
return;
236
}
237
238
sc->attach_done = true;
239
240
free_clkdef(&sc->div_def.clkdef);
241
}
242
243
static device_method_t ti_divider_methods[] = {
244
/* Device interface */
245
DEVMETHOD(device_probe, ti_divider_probe),
246
DEVMETHOD(device_attach, ti_divider_attach),
247
DEVMETHOD(device_detach, ti_divider_detach),
248
249
/* Bus interface */
250
DEVMETHOD(bus_new_pass, ti_divider_new_pass),
251
252
DEVMETHOD_END
253
};
254
255
DEFINE_CLASS_0(ti_divider, ti_divider_driver, ti_divider_methods,
256
sizeof(struct ti_divider_softc));
257
258
EARLY_DRIVER_MODULE(ti_divider, simplebus, ti_divider_driver, 0, 0,
259
BUS_PASS_BUS + BUS_PASS_ORDER_MIDDLE);
260
MODULE_VERSION(ti_divider, 1);
261
262