]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/arm/allwinner/clk/aw_cpuclk.c
Merge llvm, clang, lld, lldb, compiler-rt and libc++ r305145, and update
[FreeBSD/FreeBSD.git] / sys / arm / allwinner / clk / aw_cpuclk.c
1 /*-
2  * Copyright (c) 2016 Jared McNeill <jmcneill@invisible.ca>
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
15  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
16  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
17  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
18  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
19  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
20  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
21  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
22  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24  * SUCH DAMAGE.
25  *
26  * $FreeBSD$
27  */
28
29 /*
30  * Allwinner CPU clock
31  */
32
33 #include <sys/cdefs.h>
34 __FBSDID("$FreeBSD$");
35
36 #include <sys/param.h>
37 #include <sys/systm.h>
38 #include <sys/bus.h>
39 #include <sys/rman.h>
40 #include <sys/kernel.h>
41 #include <sys/module.h>
42 #include <machine/bus.h>
43
44 #include <dev/ofw/ofw_bus.h>
45 #include <dev/ofw/ofw_bus_subr.h>
46 #include <dev/ofw/ofw_subr.h>
47
48 #include <dev/extres/clk/clk_mux.h>
49
50 #define A10_CPU_CLK_SRC_SEL_WIDTH       2
51 #define A10_CPU_CLK_SRC_SEL_SHIFT       16
52
53 #define A83T_Cx_CLK_SRC_SEL_WIDTH       1
54 #define A83T_C0_CLK_SRC_SEL_SHIFT       12
55 #define A83T_C1_CLK_SRC_SEL_SHIFT       28
56
57 struct aw_cpuclk_config {
58         u_int           width;
59         u_int           shift;
60 };
61
62 static struct aw_cpuclk_config a10_config = {
63         .width = A10_CPU_CLK_SRC_SEL_WIDTH,
64         .shift = A10_CPU_CLK_SRC_SEL_SHIFT,
65 };
66
67 static struct aw_cpuclk_config a83t_c0_config = {
68         .width = A83T_Cx_CLK_SRC_SEL_WIDTH,
69         .shift = A83T_C0_CLK_SRC_SEL_SHIFT,
70 };
71
72 static struct aw_cpuclk_config a83t_c1_config = {
73         .width = A83T_Cx_CLK_SRC_SEL_WIDTH,
74         .shift = A83T_C1_CLK_SRC_SEL_SHIFT,
75 };
76
77 static struct ofw_compat_data compat_data[] = {
78         { "allwinner,sun4i-a10-cpu-clk",        (uintptr_t)&a10_config },
79         { "allwinner,sun8i-a83t-c0cpu-clk",     (uintptr_t)&a83t_c0_config },
80         { "allwinner,sun8i-a83t-c1cpu-clk",     (uintptr_t)&a83t_c1_config },
81         { NULL,                                 (uintptr_t)NULL }
82 };
83
84 #define CPUCLK_CONF(d)          \
85         (void *)ofw_bus_search_compatible((d), compat_data)->ocd_data
86
87 static int
88 aw_cpuclk_probe(device_t dev)
89 {
90         if (!ofw_bus_status_okay(dev))
91                 return (ENXIO);
92
93         if (CPUCLK_CONF(dev) == NULL)
94                 return (ENXIO);
95
96         device_set_desc(dev, "Allwinner CPU Clock");
97         return (BUS_PROBE_DEFAULT);
98 }
99
100 static int
101 aw_cpuclk_attach(device_t dev)
102 {
103         struct clk_mux_def def;
104         struct clkdom *clkdom;
105         struct aw_cpuclk_config *conf;
106         bus_addr_t paddr;
107         bus_size_t psize;
108         phandle_t node;
109         int error, ncells, i;
110         clk_t clk;
111
112         node = ofw_bus_get_node(dev);
113         conf = CPUCLK_CONF(dev);
114
115         if (ofw_reg_to_paddr(node, 0, &paddr, &psize, NULL) != 0) {
116                 device_printf(dev, "cannot parse 'reg' property\n");
117                 return (ENXIO);
118         }
119
120         error = ofw_bus_parse_xref_list_get_length(node, "clocks",
121             "#clock-cells", &ncells);
122         if (error != 0) {
123                 device_printf(dev, "cannot get clock count\n");
124                 return (error);
125         }
126
127         clkdom = clkdom_create(dev);
128
129         memset(&def, 0, sizeof(def));
130         def.clkdef.id = 1;
131         def.clkdef.parent_names = malloc(sizeof(char *) * ncells, M_OFWPROP,
132             M_WAITOK);
133         for (i = 0; i < ncells; i++) {
134                 error = clk_get_by_ofw_index(dev, 0, i, &clk);
135                 if (error != 0) {
136                         device_printf(dev, "cannot get clock %d\n", i);
137                         goto fail;
138                 }
139                 def.clkdef.parent_names[i] = clk_get_name(clk);
140                 clk_release(clk);
141         }
142         def.clkdef.parent_cnt = ncells;
143         def.offset = paddr;
144         def.shift = conf->shift;
145         def.width = conf->width;
146
147         error = clk_parse_ofw_clk_name(dev, node, &def.clkdef.name);
148         if (error != 0) {
149                 device_printf(dev, "cannot parse clock name\n");
150                 error = ENXIO;
151                 goto fail;
152         }
153
154         error = clknode_mux_register(clkdom, &def);
155         if (error != 0) {
156                 device_printf(dev, "cannot register mux clock\n");
157                 error = ENXIO;
158                 goto fail;
159         }
160
161         if (clkdom_finit(clkdom) != 0) {
162                 device_printf(dev, "cannot finalize clkdom initialization\n");
163                 error = ENXIO;
164                 goto fail;
165         }
166
167         OF_prop_free(__DECONST(char *, def.clkdef.parent_names));
168         OF_prop_free(__DECONST(char *, def.clkdef.name));
169
170         if (bootverbose)
171                 clkdom_dump(clkdom);
172
173         return (0);
174
175 fail:
176         OF_prop_free(__DECONST(char *, def.clkdef.name));
177         return (error);
178 }
179
180 static device_method_t aw_cpuclk_methods[] = {
181         /* Device interface */
182         DEVMETHOD(device_probe,         aw_cpuclk_probe),
183         DEVMETHOD(device_attach,        aw_cpuclk_attach),
184
185         DEVMETHOD_END
186 };
187
188 static driver_t aw_cpuclk_driver = {
189         "aw_cpuclk",
190         aw_cpuclk_methods,
191         0
192 };
193
194 static devclass_t aw_cpuclk_devclass;
195
196 EARLY_DRIVER_MODULE(aw_cpuclk, simplebus, aw_cpuclk_driver,
197     aw_cpuclk_devclass, 0, 0, BUS_PASS_BUS + BUS_PASS_ORDER_MIDDLE);