1cd32ac64SAdrian Chadd /*-
24d846d26SWarner Losh * SPDX-License-Identifier: BSD-2-Clause
3cd32ac64SAdrian Chadd *
4cd32ac64SAdrian Chadd * Copyright (c) 2021, Adrian Chadd <adrian@FreeBSD.org>
5cd32ac64SAdrian Chadd *
6cd32ac64SAdrian Chadd * Redistribution and use in source and binary forms, with or without
7cd32ac64SAdrian Chadd * modification, are permitted provided that the following conditions
8cd32ac64SAdrian Chadd * are met:
9cd32ac64SAdrian Chadd * 1. Redistributions of source code must retain the above copyright
10cd32ac64SAdrian Chadd * notice unmodified, this list of conditions, and the following
11cd32ac64SAdrian Chadd * disclaimer.
12cd32ac64SAdrian Chadd * 2. Redistributions in binary form must reproduce the above copyright
13cd32ac64SAdrian Chadd * notice, this list of conditions and the following disclaimer in the
14cd32ac64SAdrian Chadd * documentation and/or other materials provided with the distribution.
15cd32ac64SAdrian Chadd *
16cd32ac64SAdrian Chadd * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17cd32ac64SAdrian Chadd * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18cd32ac64SAdrian Chadd * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19cd32ac64SAdrian Chadd * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20cd32ac64SAdrian Chadd * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
21cd32ac64SAdrian Chadd * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22cd32ac64SAdrian Chadd * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23cd32ac64SAdrian Chadd * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24cd32ac64SAdrian Chadd * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
25cd32ac64SAdrian Chadd * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26cd32ac64SAdrian Chadd */
27cd32ac64SAdrian Chadd
28cd32ac64SAdrian Chadd /* Driver for Qualcomm IPQ4018 clock and reset device */
29cd32ac64SAdrian Chadd
30cd32ac64SAdrian Chadd #include <sys/param.h>
31cd32ac64SAdrian Chadd #include <sys/kernel.h>
32cd32ac64SAdrian Chadd #include <sys/malloc.h>
33cd32ac64SAdrian Chadd #include <sys/module.h>
34cd32ac64SAdrian Chadd #include <sys/sglist.h>
35cd32ac64SAdrian Chadd #include <sys/random.h>
36cd32ac64SAdrian Chadd #include <sys/stdatomic.h>
37cd32ac64SAdrian Chadd #include <sys/mutex.h>
38cd32ac64SAdrian Chadd
39cd32ac64SAdrian Chadd #include <machine/bus.h>
40cd32ac64SAdrian Chadd #include <machine/resource.h>
41cd32ac64SAdrian Chadd #include <sys/bus.h>
42cd32ac64SAdrian Chadd
43cd32ac64SAdrian Chadd #include <dev/fdt/fdt_common.h>
44cd32ac64SAdrian Chadd #include <dev/ofw/ofw_bus.h>
45cd32ac64SAdrian Chadd #include <dev/ofw/ofw_bus_subr.h>
46cd32ac64SAdrian Chadd
47*1f469a9fSEmmanuel Vadot #include <dev/hwreset/hwreset.h>
48cd32ac64SAdrian Chadd
49cd32ac64SAdrian Chadd #include "clkdev_if.h"
50cd32ac64SAdrian Chadd #include "hwreset_if.h"
51cd32ac64SAdrian Chadd
52cd32ac64SAdrian Chadd #include <dt-bindings/clock/qcom,gcc-ipq4019.h>
53cd32ac64SAdrian Chadd
54cd32ac64SAdrian Chadd #include "qcom_gcc_ipq4018_var.h"
55cd32ac64SAdrian Chadd
56cd32ac64SAdrian Chadd
57cd32ac64SAdrian Chadd static int qcom_gcc_ipq4018_modevent(module_t, int, void *);
58cd32ac64SAdrian Chadd
59cd32ac64SAdrian Chadd static int qcom_gcc_ipq4018_probe(device_t);
60cd32ac64SAdrian Chadd static int qcom_gcc_ipq4018_attach(device_t);
61cd32ac64SAdrian Chadd static int qcom_gcc_ipq4018_detach(device_t);
62cd32ac64SAdrian Chadd
63cd32ac64SAdrian Chadd static int
qcom_gcc_ipq4018_modevent(module_t mod,int type,void * unused)64cd32ac64SAdrian Chadd qcom_gcc_ipq4018_modevent(module_t mod, int type, void *unused)
65cd32ac64SAdrian Chadd {
66cd32ac64SAdrian Chadd int error;
67cd32ac64SAdrian Chadd
68cd32ac64SAdrian Chadd switch (type) {
69cd32ac64SAdrian Chadd case MOD_LOAD:
70cd32ac64SAdrian Chadd case MOD_QUIESCE:
71cd32ac64SAdrian Chadd case MOD_UNLOAD:
72cd32ac64SAdrian Chadd case MOD_SHUTDOWN:
73cd32ac64SAdrian Chadd error = 0;
74cd32ac64SAdrian Chadd break;
75cd32ac64SAdrian Chadd default:
76cd32ac64SAdrian Chadd error = EOPNOTSUPP;
77cd32ac64SAdrian Chadd break;
78cd32ac64SAdrian Chadd }
79cd32ac64SAdrian Chadd
80cd32ac64SAdrian Chadd return (error);
81cd32ac64SAdrian Chadd }
82cd32ac64SAdrian Chadd
83cd32ac64SAdrian Chadd static int
qcom_gcc_ipq4018_probe(device_t dev)84cd32ac64SAdrian Chadd qcom_gcc_ipq4018_probe(device_t dev)
85cd32ac64SAdrian Chadd {
86cd32ac64SAdrian Chadd if (! ofw_bus_status_okay(dev))
87cd32ac64SAdrian Chadd return (ENXIO);
88cd32ac64SAdrian Chadd
89cd32ac64SAdrian Chadd if (ofw_bus_is_compatible(dev, "qcom,gcc-ipq4019") == 0)
90cd32ac64SAdrian Chadd return (ENXIO);
91cd32ac64SAdrian Chadd
92cd32ac64SAdrian Chadd return (0);
93cd32ac64SAdrian Chadd }
94cd32ac64SAdrian Chadd
95cd32ac64SAdrian Chadd static int
qcom_gcc_ipq4018_attach(device_t dev)96cd32ac64SAdrian Chadd qcom_gcc_ipq4018_attach(device_t dev)
97cd32ac64SAdrian Chadd {
98cd32ac64SAdrian Chadd struct qcom_gcc_ipq4018_softc *sc;
99cd32ac64SAdrian Chadd
100cd32ac64SAdrian Chadd sc = device_get_softc(dev);
101cd32ac64SAdrian Chadd
102cd32ac64SAdrian Chadd /* Found a compatible device! */
103cd32ac64SAdrian Chadd sc->dev = dev;
104cd32ac64SAdrian Chadd
105cd32ac64SAdrian Chadd sc->reg_rid = 0;
106cd32ac64SAdrian Chadd sc->reg = bus_alloc_resource_anywhere(dev, SYS_RES_MEMORY,
107cd32ac64SAdrian Chadd &sc->reg_rid, 0x60000, RF_ACTIVE);
108cd32ac64SAdrian Chadd if (sc->reg == NULL) {
109cd32ac64SAdrian Chadd device_printf(dev, "Couldn't allocate memory resource!\n");
110cd32ac64SAdrian Chadd return (ENXIO);
111cd32ac64SAdrian Chadd }
112cd32ac64SAdrian Chadd
113cd32ac64SAdrian Chadd device_set_desc(dev, "Qualcomm IPQ4018 Clock/Reset Controller");
114cd32ac64SAdrian Chadd
115cd32ac64SAdrian Chadd mtx_init(&sc->mtx, device_get_nameunit(dev), NULL, MTX_DEF);
116cd32ac64SAdrian Chadd
117cd32ac64SAdrian Chadd /*
118cd32ac64SAdrian Chadd * Register as a reset provider.
119cd32ac64SAdrian Chadd */
120cd32ac64SAdrian Chadd hwreset_register_ofw_provider(dev);
121cd32ac64SAdrian Chadd
122cd32ac64SAdrian Chadd /*
123cd32ac64SAdrian Chadd * Setup and register as a clock provider.
124cd32ac64SAdrian Chadd */
125cd32ac64SAdrian Chadd qcom_gcc_ipq4018_clock_setup(sc);
126cd32ac64SAdrian Chadd
127cd32ac64SAdrian Chadd return (0);
128cd32ac64SAdrian Chadd }
129cd32ac64SAdrian Chadd
130cd32ac64SAdrian Chadd static int
qcom_gcc_ipq4018_detach(device_t dev)131cd32ac64SAdrian Chadd qcom_gcc_ipq4018_detach(device_t dev)
132cd32ac64SAdrian Chadd {
133cd32ac64SAdrian Chadd struct qcom_gcc_ipq4018_softc *sc;
134cd32ac64SAdrian Chadd
135cd32ac64SAdrian Chadd sc = device_get_softc(dev);
136cd32ac64SAdrian Chadd
137cd32ac64SAdrian Chadd /*
138cd32ac64SAdrian Chadd * TBD - deregistering reset/clock resources.
139cd32ac64SAdrian Chadd */
140cd32ac64SAdrian Chadd
141cd32ac64SAdrian Chadd if (sc->reg != NULL) {
142cd32ac64SAdrian Chadd bus_release_resource(sc->dev, SYS_RES_MEMORY,
143cd32ac64SAdrian Chadd sc->reg_rid, sc->reg);
144cd32ac64SAdrian Chadd }
145cd32ac64SAdrian Chadd return (0);
146cd32ac64SAdrian Chadd }
147cd32ac64SAdrian Chadd
148cd32ac64SAdrian Chadd static device_method_t qcom_gcc_ipq4018_methods[] = {
149cd32ac64SAdrian Chadd /* Device methods. */
150cd32ac64SAdrian Chadd DEVMETHOD(device_probe, qcom_gcc_ipq4018_probe),
151cd32ac64SAdrian Chadd DEVMETHOD(device_attach, qcom_gcc_ipq4018_attach),
152cd32ac64SAdrian Chadd DEVMETHOD(device_detach, qcom_gcc_ipq4018_detach),
153cd32ac64SAdrian Chadd
154cd32ac64SAdrian Chadd /* Reset interface */
155cd32ac64SAdrian Chadd DEVMETHOD(hwreset_assert, qcom_gcc_ipq4018_hwreset_assert),
156cd32ac64SAdrian Chadd DEVMETHOD(hwreset_is_asserted, qcom_gcc_ipq4018_hwreset_is_asserted),
157cd32ac64SAdrian Chadd
158cd32ac64SAdrian Chadd /* Clock interface */
159cd32ac64SAdrian Chadd DEVMETHOD(clkdev_read_4, qcom_gcc_ipq4018_clock_read),
160cd32ac64SAdrian Chadd DEVMETHOD(clkdev_write_4, qcom_gcc_ipq4018_clock_write),
161cd32ac64SAdrian Chadd DEVMETHOD(clkdev_modify_4, qcom_gcc_ipq4018_clock_modify),
162cd32ac64SAdrian Chadd DEVMETHOD(clkdev_device_lock, qcom_gcc_ipq4018_clock_lock),
163cd32ac64SAdrian Chadd DEVMETHOD(clkdev_device_unlock, qcom_gcc_ipq4018_clock_unlock),
164cd32ac64SAdrian Chadd
165cd32ac64SAdrian Chadd DEVMETHOD_END
166cd32ac64SAdrian Chadd };
167cd32ac64SAdrian Chadd
168cd32ac64SAdrian Chadd static driver_t qcom_gcc_ipq4018_driver = {
169cd32ac64SAdrian Chadd "qcom_gcc",
170cd32ac64SAdrian Chadd qcom_gcc_ipq4018_methods,
171cd32ac64SAdrian Chadd sizeof(struct qcom_gcc_ipq4018_softc)
172cd32ac64SAdrian Chadd };
173cd32ac64SAdrian Chadd
174cd32ac64SAdrian Chadd EARLY_DRIVER_MODULE(qcom_gcc_ipq4018, simplebus, qcom_gcc_ipq4018_driver,
17553e1cbefSJohn Baldwin qcom_gcc_ipq4018_modevent, NULL, BUS_PASS_CPU + BUS_PASS_ORDER_EARLY);
176cd32ac64SAdrian Chadd EARLY_DRIVER_MODULE(qcom_gcc_ipq4018, ofwbus, qcom_gcc_ipq4018_driver,
17753e1cbefSJohn Baldwin qcom_gcc_ipq4018_modevent, NULL, BUS_PASS_CPU + BUS_PASS_ORDER_EARLY);
178cd32ac64SAdrian Chadd MODULE_VERSION(qcom_gcc_ipq4018, 1);
179