1297667Ssgalabov/*-
2297667Ssgalabov * Copyright (c) 2016 Stanislav Galabov
3297667Ssgalabov * All rights reserved.
4297667Ssgalabov *
5297667Ssgalabov * Redistribution and use in source and binary forms, with or without
6297667Ssgalabov * modification, are permitted provided that the following conditions
7297667Ssgalabov * are met:
8297667Ssgalabov * 1. Redistributions of source code must retain the above copyright
9297667Ssgalabov *    notice, this list of conditions, and the following disclaimer,
10297667Ssgalabov *    without modification, immediately at the beginning of the file.
11297667Ssgalabov * 2. The name of the author may not be used to endorse or promote products
12297667Ssgalabov *    derived from this software without specific prior written permission.
13297667Ssgalabov *
14297667Ssgalabov * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15297667Ssgalabov * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16297667Ssgalabov * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17297667Ssgalabov * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
18297667Ssgalabov * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19297667Ssgalabov * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20297667Ssgalabov * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21297667Ssgalabov * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22297667Ssgalabov * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23297667Ssgalabov * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24297667Ssgalabov * SUCH DAMAGE.
25297667Ssgalabov *
26297667Ssgalabov */
27297667Ssgalabov
28297667Ssgalabov#include <sys/cdefs.h>
29297667Ssgalabov__FBSDID("$FreeBSD$");
30297667Ssgalabov
31297667Ssgalabov#include <sys/param.h>
32297667Ssgalabov#include <sys/kernel.h>
33297667Ssgalabov#include <sys/bus.h>
34297667Ssgalabov#include <sys/module.h>
35297667Ssgalabov
36297667Ssgalabov#include <dev/fdt/fdt_common.h>
37297667Ssgalabov#include <dev/ofw/openfirm.h>
38297667Ssgalabov#include <dev/ofw/ofw_bus.h>
39297667Ssgalabov#include <dev/ofw/ofw_bus_subr.h>
40297667Ssgalabov#include <dev/fdt/fdt_clock.h>
41297667Ssgalabov
42297667Ssgalabov#include <mips/mediatek/mtk_sysctl.h>
43297667Ssgalabov
44297667Ssgalabov#include "fdt_clock_if.h"
45297667Ssgalabov
46297667Ssgalabovstatic const struct ofw_compat_data compat_data[] = {
47297667Ssgalabov	{ "ralink,rt2880-clock",	1 },
48297667Ssgalabov
49297667Ssgalabov	/* Sentinel */
50297667Ssgalabov	{ NULL,				0 }
51297667Ssgalabov};
52297667Ssgalabov
53297667Ssgalabovstatic int
54297667Ssgalabovmtk_clock_probe(device_t dev)
55297667Ssgalabov{
56297667Ssgalabov
57297667Ssgalabov	if (!ofw_bus_status_okay(dev))
58297667Ssgalabov		return (ENXIO);
59297667Ssgalabov
60297667Ssgalabov	if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0)
61297667Ssgalabov		return (ENXIO);
62297667Ssgalabov
63297667Ssgalabov	device_set_desc(dev, "MTK Clock Controller");
64297667Ssgalabov
65297667Ssgalabov	return (0);
66297667Ssgalabov}
67297667Ssgalabov
68297667Ssgalabovstatic int
69297667Ssgalabovmtk_clock_attach(device_t dev)
70297667Ssgalabov{
71297667Ssgalabov
72297667Ssgalabov	if (device_get_unit(dev) != 0) {
73297667Ssgalabov		device_printf(dev, "Only one clock control allowed\n");
74297667Ssgalabov		return (ENXIO);
75297667Ssgalabov	}
76297667Ssgalabov
77297667Ssgalabov	fdt_clock_register_provider(dev);
78297667Ssgalabov
79297667Ssgalabov	return (0);
80297667Ssgalabov}
81297667Ssgalabov
82297667Ssgalabov#define CLOCK_ENABLE	1
83297667Ssgalabov#define CLOCK_DISABLE	0
84297667Ssgalabov
85297667Ssgalabovstatic int
86297667Ssgalabovmtk_clock_set(device_t dev, int index, int value)
87297667Ssgalabov{
88297667Ssgalabov	uint32_t mask;
89297667Ssgalabov
90297667Ssgalabov	/* Clock config register holds 32 clock gating bits */
91297667Ssgalabov	if (index < 0 || index > 31)
92297667Ssgalabov		return (EINVAL);
93297667Ssgalabov
94297667Ssgalabov	mask = (1u << index);
95297667Ssgalabov
96297667Ssgalabov	if (value == CLOCK_ENABLE)
97297667Ssgalabov		mtk_sysctl_clr_set(SYSCTL_CLKCFG1, 0, mask);
98297667Ssgalabov	else
99297667Ssgalabov		mtk_sysctl_clr_set(SYSCTL_CLKCFG1, mask, 0);
100297667Ssgalabov
101297667Ssgalabov	return (0);
102297667Ssgalabov}
103297667Ssgalabov
104297667Ssgalabovstatic int
105297667Ssgalabovmtk_clock_enable(device_t dev, int index)
106297667Ssgalabov{
107297667Ssgalabov
108297667Ssgalabov	return mtk_clock_set(dev, index, CLOCK_ENABLE);
109297667Ssgalabov}
110297667Ssgalabov
111297667Ssgalabovstatic int
112297667Ssgalabovmtk_clock_disable(device_t dev, int index)
113297667Ssgalabov{
114297667Ssgalabov
115297667Ssgalabov	return mtk_clock_set(dev, index, CLOCK_DISABLE);
116297667Ssgalabov}
117297667Ssgalabov
118297667Ssgalabovstatic int
119297667Ssgalabovmtk_clock_get_info(device_t dev, int index, struct fdt_clock_info *info)
120297667Ssgalabov{
121297667Ssgalabov	uint32_t mask;
122297667Ssgalabov
123297667Ssgalabov	if (index < 0 || index > 31 || info == NULL)
124297667Ssgalabov		return (EINVAL);
125297667Ssgalabov
126297667Ssgalabov	if (mtk_sysctl_get(SYSCTL_CLKCFG1) & mask)
127297667Ssgalabov		info->flags = FDT_CIFLAG_RUNNING;
128297667Ssgalabov	else
129297667Ssgalabov		info->flags = 0;
130297667Ssgalabov
131297667Ssgalabov	return (0);
132297667Ssgalabov}
133297667Ssgalabov
134297667Ssgalabovstatic device_method_t mtk_clock_methods[] = {
135297667Ssgalabov	DEVMETHOD(device_probe,		mtk_clock_probe),
136297667Ssgalabov	DEVMETHOD(device_attach,	mtk_clock_attach),
137297667Ssgalabov
138297667Ssgalabov	/* fdt_clock interface */
139297667Ssgalabov	DEVMETHOD(fdt_clock_enable,	mtk_clock_enable),
140297667Ssgalabov	DEVMETHOD(fdt_clock_disable,	mtk_clock_disable),
141297667Ssgalabov	DEVMETHOD(fdt_clock_get_info,	mtk_clock_get_info),
142297667Ssgalabov
143297667Ssgalabov	DEVMETHOD_END
144297667Ssgalabov};
145297667Ssgalabov
146297667Ssgalabovstatic driver_t mtk_clock_driver = {
147297667Ssgalabov	"clkctrl",
148297667Ssgalabov	mtk_clock_methods,
149297667Ssgalabov	0,
150297667Ssgalabov};
151297667Ssgalabovstatic devclass_t mtk_clock_devclass;
152297667Ssgalabov
153297667SsgalabovEARLY_DRIVER_MODULE(mtk_clock, simplebus, mtk_clock_driver, mtk_clock_devclass,
154297667Ssgalabov    0, 0, BUS_PASS_INTERRUPT + BUS_PASS_ORDER_EARLY);
155297667Ssgalabov
156297667SsgalabovMODULE_DEPEND(mtk_clock, mtk_sysctl, 1, 1, 1);
157