1 /*-
2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3 *
4 * Copyright (c) 2017 Poul-Henning Kamp <phk@FreeBSD.org>
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 AND CONTRIBUTORS ``AS IS'' AND
16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23 * LIABILITY, 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
29 #include <sys/cdefs.h>
30 __FBSDID("$FreeBSD$");
31
32 #include <sys/param.h>
33 #include <sys/systm.h>
34 #include <sys/bus.h>
35 #include <sys/cpu.h>
36 #include <sys/kernel.h>
37 #include <sys/lock.h>
38 #include <sys/malloc.h>
39 #include <sys/module.h>
40 #include <sys/mutex.h>
41 #include <sys/rman.h>
42 #include <sys/sema.h>
43 #include <sys/sysctl.h>
44
45 #include <machine/bus.h>
46 #include <machine/cpu.h>
47
48 #include <dev/ofw/ofw_bus.h>
49 #include <dev/ofw/ofw_bus_subr.h>
50
51 #include <arm/broadcom/bcm2835/bcm2835_clkman.h>
52
53 static struct ofw_compat_data compat_data[] = {
54 {"brcm,bcm2711-cprman", 1},
55 {"brcm,bcm2835-cprman", 1},
56 {"broadcom,bcm2835-cprman", 1},
57 {NULL, 0}
58 };
59
60 struct bcm2835_clkman_softc {
61 device_t sc_dev;
62
63 struct resource * sc_m_res;
64 bus_space_tag_t sc_m_bst;
65 bus_space_handle_t sc_m_bsh;
66 };
67
68 #define BCM_CLKMAN_WRITE(_sc, _off, _val) \
69 bus_space_write_4(_sc->sc_m_bst, _sc->sc_m_bsh, _off, _val)
70 #define BCM_CLKMAN_READ(_sc, _off) \
71 bus_space_read_4(_sc->sc_m_bst, _sc->sc_m_bsh, _off)
72
73 #define W_CMCLK(_sc, unit, _val) BCM_CLKMAN_WRITE(_sc, unit, 0x5a000000 | (_val))
74 #define R_CMCLK(_sc, unit) BCM_CLKMAN_READ(_sc, unit)
75 #define W_CMDIV(_sc, unit, _val) BCM_CLKMAN_WRITE(_sc, (unit) + 4, 0x5a000000 | (_val))
76 #define R_CMDIV(_sc, unit) BCM_CLKMAN_READ(_sc, (unit) + 4)
77
78 static int
79 bcm2835_clkman_probe(device_t dev)
80 {
81
82 if (!ofw_bus_status_okay(dev))
83 return (ENXIO);
84
85 if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0)
86 return (ENXIO);
87
88 device_set_desc(dev, "BCM283x Clock Manager");
89
90 return (BUS_PROBE_DEFAULT);
91 }
92
93 static int
94 bcm2835_clkman_attach(device_t dev)
95 {
96 struct bcm2835_clkman_softc *sc;
97 int rid;
98
99 if (device_get_unit(dev) != 0) {
100 device_printf(dev, "only one clk manager supported\n");
101 return (ENXIO);
102 }
103
104 sc = device_get_softc(dev);
105 sc->sc_dev = dev;
106
107 rid = 0;
108 sc->sc_m_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
109 RF_ACTIVE);
110 if (!sc->sc_m_res) {
111 device_printf(dev, "cannot allocate memory window\n");
112 return (ENXIO);
113 }
114
115 sc->sc_m_bst = rman_get_bustag(sc->sc_m_res);
116 sc->sc_m_bsh = rman_get_bushandle(sc->sc_m_res);
117
118 return (bus_generic_attach(dev));
119 }
120
121 uint32_t
122 bcm2835_clkman_set_frequency(device_t dev, uint32_t unit, uint32_t hz)
123 {
124 struct bcm2835_clkman_softc *sc;
125 int i;
126 uint32_t u;
127
128 sc = device_get_softc(dev);
129
130 if (unit != BCM_PWM_CLKSRC) {
131 device_printf(sc->sc_dev,
132 "Unsupported unit 0x%x", unit);
133 return (0);
134 }
135
136 W_CMCLK(sc, unit, 6);
137 for (i = 0; i < 10; i++) {
138 u = R_CMCLK(sc, unit);
139 if (!(u&0x80))
140 break;
141 DELAY(1000);
142 }
143 if (u & 0x80) {
144 device_printf(sc->sc_dev,
145 "Failed to stop clock for unit 0x%x", unit);
146 return (0);
147 }
148 if (hz == 0)
149 return (0);
150
151 u = 500000000/hz;
152 if (u < 4) {
153 device_printf(sc->sc_dev,
154 "Frequency too high for unit 0x%x (max: 125 MHz)",
155 unit);
156 return (0);
157 }
158 if (u > 0xfff) {
159 device_printf(sc->sc_dev,
160 "Frequency too low for unit 0x%x (min: 123 kHz)",
161 unit);
162 return (0);
163 }
164 hz = 500000000/u;
165 W_CMDIV(sc, unit, u << 12);
166
167 W_CMCLK(sc, unit, 0x16);
168 for (i = 0; i < 10; i++) {
169 u = R_CMCLK(sc, unit);
170 if ((u&0x80))
171 break;
172 DELAY(1000);
173 }
174 if (!(u & 0x80)) {
175 device_printf(sc->sc_dev,
176 "Failed to start clock for unit 0x%x", unit);
177 return (0);
178 }
179 return (hz);
180 }
181
182 static int
183 bcm2835_clkman_detach(device_t dev)
184 {
185 struct bcm2835_clkman_softc *sc;
186
187 bus_generic_detach(dev);
188
189 sc = device_get_softc(dev);
190 if (sc->sc_m_res)
191 bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->sc_m_res);
192
193 return (0);
194 }
195
196 static device_method_t bcm2835_clkman_methods[] = {
197 /* Device interface */
198 DEVMETHOD(device_probe, bcm2835_clkman_probe),
199 DEVMETHOD(device_attach, bcm2835_clkman_attach),
200 DEVMETHOD(device_detach, bcm2835_clkman_detach),
201
202 DEVMETHOD_END
203 };
204
205 static driver_t bcm2835_clkman_driver = {
206 "bcm2835_clkman",
207 bcm2835_clkman_methods,
208 sizeof(struct bcm2835_clkman_softc),
209 };
210
211 DRIVER_MODULE(bcm2835_clkman, simplebus, bcm2835_clkman_driver, 0, 0);
212 MODULE_VERSION(bcm2835_clkman, 1);
Cache object: 708e383a64780ca670a2aacecdc30b8b
|