Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/sys/arm/ti/clk/ti_clkctrl.c
39507 views
1
/*-
2
* SPDX-License-Identifier: BSD-2-Clause
3
*
4
* Copyright 2016 Michal Meloun <[email protected]>
5
*
6
* Copyright (c) 2020 Oskar Holmlund <[email protected]>
7
*
8
* Redistribution and use in source and binary forms, with or without
9
* modification, are permitted provided that the following conditions
10
* are met:
11
* 1. Redistributions of source code must retain the above copyright
12
* notice, this list of conditions and the following disclaimer.
13
* 2. Redistributions in binary form must reproduce the above copyright
14
* notice, this list of conditions and the following disclaimer in the
15
* documentation and/or other materials provided with the distribution.
16
*
17
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
22
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
23
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
24
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
25
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27
* SUCH DAMAGE.
28
*/
29
30
#include <sys/param.h>
31
#include <sys/systm.h>
32
#include <sys/bus.h>
33
#include <sys/fbio.h>
34
#include <sys/kernel.h>
35
#include <sys/module.h>
36
#include <sys/rman.h>
37
#include <sys/resource.h>
38
#include <machine/bus.h>
39
#include <vm/vm.h>
40
#include <vm/vm_extern.h>
41
#include <vm/vm_kern.h>
42
#include <vm/pmap.h>
43
44
#include <dev/fdt/simplebus.h>
45
46
#include <dev/ofw/ofw_bus.h>
47
#include <dev/ofw/ofw_bus_subr.h>
48
49
#include <arm/ti/clk/ti_clk_clkctrl.h>
50
#include <arm/ti/ti_omap4_cm.h>
51
#include <arm/ti/ti_cpuid.h>
52
53
#if 0
54
#define DPRINTF(dev, msg...) device_printf(dev, msg)
55
#else
56
#define DPRINTF(dev, msg...)
57
#endif
58
59
#define L4LS_CLKCTRL_38 2
60
#define L4_WKUP_CLKCTRL_0 1
61
#define NO_SPECIAL_REG 0
62
63
/* Documentation/devicetree/bindings/clock/ti-clkctrl.txt */
64
65
#define TI_CLKCTRL_L4_WKUP 5
66
#define TI_CLKCTRL_L4_SECURE 4
67
#define TI_CLKCTRL_L4_PER 3
68
#define TI_CLKCTRL_L4_CFG 2
69
#define TI_CLKCTRL 1
70
#define TI_CLKCTRL_END 0
71
72
static struct ofw_compat_data compat_data[] = {
73
{ "ti,clkctrl-l4-wkup", TI_CLKCTRL_L4_WKUP },
74
{ "ti,clkctrl-l4-secure", TI_CLKCTRL_L4_SECURE },
75
{ "ti,clkctrl-l4-per", TI_CLKCTRL_L4_PER },
76
{ "ti,clkctrl-l4-cfg", TI_CLKCTRL_L4_CFG },
77
{ "ti,clkctrl", TI_CLKCTRL },
78
{ NULL, TI_CLKCTRL_END }
79
};
80
81
struct ti_clkctrl_softc {
82
device_t dev;
83
84
struct clkdom *clkdom;
85
};
86
87
static int ti_clkctrl_probe(device_t dev);
88
static int ti_clkctrl_attach(device_t dev);
89
static int ti_clkctrl_detach(device_t dev);
90
int clkctrl_ofw_map(struct clkdom *clkdom, uint32_t ncells,
91
phandle_t *cells, struct clknode **clk);
92
static int
93
create_clkctrl(struct ti_clkctrl_softc *sc, cell_t *reg, uint32_t index, uint32_t reg_offset,
94
uint64_t parent_offset, const char *org_name, bool special_gdbclk_reg);
95
96
static int
97
ti_clkctrl_probe(device_t dev)
98
{
99
if (!ofw_bus_status_okay(dev))
100
return (ENXIO);
101
102
if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0)
103
return (ENXIO);
104
105
device_set_desc(dev, "TI clkctrl");
106
107
return (BUS_PROBE_DEFAULT);
108
}
109
110
static int
111
ti_clkctrl_attach(device_t dev)
112
{
113
struct ti_clkctrl_softc *sc;
114
phandle_t node;
115
cell_t *reg;
116
ssize_t numbytes_reg;
117
int num_reg, err, ti_clock_cells;
118
uint32_t index, reg_offset, reg_address;
119
const char *org_name;
120
uint64_t parent_offset;
121
uint8_t special_reg = NO_SPECIAL_REG;
122
123
sc = device_get_softc(dev);
124
sc->dev = dev;
125
node = ofw_bus_get_node(dev);
126
127
/* Sanity check */
128
err = OF_searchencprop(node, "#clock-cells",
129
&ti_clock_cells, sizeof(ti_clock_cells));
130
if (err == -1) {
131
device_printf(sc->dev, "Failed to get #clock-cells\n");
132
return (ENXIO);
133
}
134
135
if (ti_clock_cells != 2) {
136
device_printf(sc->dev, "clock cells(%d) != 2\n",
137
ti_clock_cells);
138
return (ENXIO);
139
}
140
141
/* Grab the content of reg properties */
142
numbytes_reg = OF_getproplen(node, "reg");
143
if (numbytes_reg == 0) {
144
device_printf(sc->dev, "reg property empty - check your devicetree\n");
145
return (ENXIO);
146
}
147
num_reg = numbytes_reg / sizeof(cell_t);
148
149
reg = malloc(numbytes_reg, M_DEVBUF, M_WAITOK);
150
OF_getencprop(node, "reg", reg, numbytes_reg);
151
152
/* Create clock domain */
153
sc->clkdom = clkdom_create(sc->dev);
154
if (sc->clkdom == NULL) {
155
free(reg, M_DEVBUF);
156
DPRINTF(sc->dev, "Failed to create clkdom\n");
157
return (ENXIO);
158
}
159
clkdom_set_ofw_mapper(sc->clkdom, clkctrl_ofw_map);
160
161
/* Create clock nodes */
162
/* name */
163
clk_parse_ofw_clk_name(sc->dev, node, &org_name);
164
165
/* Get parent range */
166
parent_offset = ti_omap4_cm_get_simplebus_base_host(device_get_parent(dev));
167
168
/* Check if this is a clkctrl with special registers like gpio */
169
switch (ti_chip()) {
170
#ifdef SOC_TI_AM335X
171
/* Checkout TRM 8.1.12.1.29 - 8.1.12.31 and 8.1.12.2.3
172
* and the DTS.
173
*/
174
case CHIP_AM335X:
175
if (strcmp(org_name, "l4ls-clkctrl@38") == 0)
176
special_reg = L4LS_CLKCTRL_38;
177
else if (strcmp(org_name, "l4-wkup-clkctrl@0") == 0)
178
special_reg = L4_WKUP_CLKCTRL_0;
179
break;
180
#endif /* SOC_TI_AM335X */
181
default:
182
break;
183
}
184
185
/* reg property has a pair of (base address, length) */
186
for (index = 0; index < num_reg; index += 2) {
187
for (reg_offset = 0; reg_offset < reg[index+1]; reg_offset += sizeof(cell_t)) {
188
err = create_clkctrl(sc, reg, index, reg_offset, parent_offset,
189
org_name, false);
190
if (err)
191
goto cleanup;
192
193
/* Create special clkctrl for GDBCLK in GPIO registers */
194
switch (special_reg) {
195
case NO_SPECIAL_REG:
196
break;
197
case L4LS_CLKCTRL_38:
198
reg_address = reg[index] + reg_offset-reg[0];
199
if (reg_address == 0x74 ||
200
reg_address == 0x78 ||
201
reg_address == 0x7C)
202
{
203
err = create_clkctrl(sc, reg, index, reg_offset,
204
parent_offset, org_name, true);
205
if (err)
206
goto cleanup;
207
}
208
break;
209
case L4_WKUP_CLKCTRL_0:
210
reg_address = reg[index] + reg_offset - reg[0];
211
if (reg_address == 0x8)
212
{
213
err = create_clkctrl(sc, reg, index, reg_offset,
214
parent_offset, org_name, true);
215
if (err)
216
goto cleanup;
217
}
218
break;
219
} /* switch (special_reg) */
220
} /* inner for */
221
} /* for */
222
223
err = clkdom_finit(sc->clkdom);
224
if (err) {
225
DPRINTF(sc->dev, "Clk domain finit fails %x.\n", err);
226
err = ENXIO;
227
goto cleanup;
228
}
229
230
cleanup:
231
OF_prop_free(__DECONST(char *, org_name));
232
233
free(reg, M_DEVBUF);
234
235
if (err)
236
return (err);
237
238
bus_attach_children(dev);
239
return (0);
240
}
241
242
static int
243
ti_clkctrl_detach(device_t dev)
244
{
245
return (EBUSY);
246
}
247
248
/* modified version of default mapper from clk.c */
249
int
250
clkctrl_ofw_map(struct clkdom *clkdom, uint32_t ncells,
251
phandle_t *cells, struct clknode **clk) {
252
if (ncells == 0)
253
*clk = clknode_find_by_id(clkdom, 1);
254
else if (ncells == 1)
255
*clk = clknode_find_by_id(clkdom, cells[0]);
256
else if (ncells == 2) {
257
/* To avoid collision with other IDs just add one.
258
* All other registers has an offset of 4 from each other.
259
*/
260
if (cells[1])
261
*clk = clknode_find_by_id(clkdom, cells[0]+1);
262
else
263
*clk = clknode_find_by_id(clkdom, cells[0]);
264
}
265
else
266
return (ERANGE);
267
268
if (*clk == NULL)
269
return (ENXIO);
270
271
return (0);
272
}
273
274
static int
275
create_clkctrl(struct ti_clkctrl_softc *sc, cell_t *reg, uint32_t index, uint32_t reg_offset,
276
uint64_t parent_offset, const char *org_name, bool special_gdbclk_reg) {
277
struct ti_clk_clkctrl_def def;
278
char *name;
279
size_t name_len;
280
int err;
281
282
name_len = strlen(org_name) + 1 + 5; /* 5 = _xxxx */
283
name = malloc(name_len, M_OFWPROP, M_WAITOK);
284
285
/*
286
* Check out XX_CLKCTRL-INDEX(offset)-macro dance in
287
* sys/gnu/dts/dts/include/dt-bindings/clock/am3.h
288
* sys/gnu/dts/dts/include/dt-bindings/clock/am4.h
289
* sys/gnu/dts/dts/include/dt-bindings/clock/dra7.h
290
* reg[0] are in practice the same as the offset described in the dts.
291
*/
292
/* special_gdbclk_reg are 0 or 1 */
293
def.clkdef.id = reg[index] + reg_offset - reg[0] + special_gdbclk_reg;
294
def.register_offset = parent_offset + reg[index] + reg_offset;
295
296
/* Indicate this clkctrl is special and dont use IDLEST/MODULEMODE */
297
def.gdbclk = special_gdbclk_reg;
298
299
/* Make up an uniq name in the namespace for each clkctrl */
300
snprintf(name, name_len, "%s_%x",
301
org_name, def.clkdef.id);
302
def.clkdef.name = (const char *) name;
303
304
DPRINTF(sc->dev, "ti_clkctrl_attach: reg[%d]: %s %x\n",
305
index, def.clkdef.name, def.clkdef.id);
306
307
/* No parent name */
308
def.clkdef.parent_cnt = 0;
309
310
/* set flags */
311
def.clkdef.flags = 0x0;
312
313
/* Register the clkctrl */
314
err = ti_clknode_clkctrl_register(sc->clkdom, &def);
315
if (err) {
316
DPRINTF(sc->dev,
317
"ti_clknode_clkctrl_register[%d:%d] failed %x\n",
318
index, reg_offset, err);
319
err = ENXIO;
320
}
321
OF_prop_free(name);
322
return (err);
323
}
324
325
static device_method_t ti_clkctrl_methods[] = {
326
/* Device interface */
327
DEVMETHOD(device_probe, ti_clkctrl_probe),
328
DEVMETHOD(device_attach, ti_clkctrl_attach),
329
DEVMETHOD(device_detach, ti_clkctrl_detach),
330
331
DEVMETHOD_END
332
};
333
334
DEFINE_CLASS_0(ti_clkctrl, ti_clkctrl_driver, ti_clkctrl_methods,
335
sizeof(struct ti_clkctrl_softc));
336
337
EARLY_DRIVER_MODULE(ti_clkctrl, simplebus, ti_clkctrl_driver, 0, 0,
338
BUS_PASS_BUS + BUS_PASS_ORDER_MIDDLE);
339
340
MODULE_VERSION(ti_clkctrl, 1);
341
342