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
41297667Ssgalabov#include <dev/fdt/fdt_pinctrl.h>
42297667Ssgalabov#include <mips/mediatek/mtk_sysctl.h>
43298348Ssgalabov#include <mips/mediatek/mtk_soc.h>
44298348Ssgalabov#include <mips/mediatek/mtk_pinctrl.h>
45297667Ssgalabov
46297667Ssgalabov#include "fdt_pinctrl_if.h"
47297667Ssgalabov
48297667Ssgalabovstatic const struct ofw_compat_data compat_data[] = {
49298348Ssgalabov	{ "ralink,rt2880-pinmux",	1 },
50297667Ssgalabov
51297667Ssgalabov	/* Sentinel */
52297667Ssgalabov	{ NULL,				0 }
53297667Ssgalabov};
54297667Ssgalabov
55297667Ssgalabovstatic int
56297667Ssgalabovmtk_pinctrl_probe(device_t dev)
57297667Ssgalabov{
58297667Ssgalabov
59297667Ssgalabov	if (!ofw_bus_status_okay(dev))
60297667Ssgalabov		return (ENXIO);
61297667Ssgalabov
62297667Ssgalabov	if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0)
63297667Ssgalabov		return (ENXIO);
64297667Ssgalabov
65297667Ssgalabov	device_set_desc(dev, "MTK Pin Controller");
66297667Ssgalabov
67297667Ssgalabov	return (0);
68297667Ssgalabov}
69297667Ssgalabov
70297667Ssgalabovstatic int
71297667Ssgalabovmtk_pinctrl_attach(device_t dev)
72297667Ssgalabov{
73297667Ssgalabov
74297667Ssgalabov	if (device_get_unit(dev) != 0) {
75297667Ssgalabov		device_printf(dev, "Only one pin control allowed\n");
76297667Ssgalabov		return (ENXIO);
77297667Ssgalabov	}
78297667Ssgalabov
79298348Ssgalabov	if (bootverbose)
80298348Ssgalabov		device_printf(dev, "GPIO mode start: 0x%08x\n",
81298348Ssgalabov		    mtk_sysctl_get(SYSCTL_GPIOMODE));
82298348Ssgalabov
83298348Ssgalabov	fdt_pinctrl_register(dev, NULL);
84297667Ssgalabov	fdt_pinctrl_configure_tree(dev);
85297667Ssgalabov
86297667Ssgalabov	if (bootverbose)
87298348Ssgalabov		device_printf(dev, "GPIO mode end  : 0x%08x\n",
88297667Ssgalabov		    mtk_sysctl_get(SYSCTL_GPIOMODE));
89297667Ssgalabov
90297667Ssgalabov	return (0);
91297667Ssgalabov}
92297667Ssgalabov
93297667Ssgalabovstatic int
94298348Ssgalabovmtk_pinctrl_process_entry(device_t dev, struct mtk_pin_group *table,
95298348Ssgalabov    const char *group, char *func)
96298348Ssgalabov{
97298348Ssgalabov	uint32_t val;
98298348Ssgalabov	int found = 0, i, j;
99298348Ssgalabov
100298348Ssgalabov	for (i = 0; table[i].name != NULL; i++) {
101298348Ssgalabov                if (strcmp(table[i].name, group) == 0) {
102298348Ssgalabov			found = 1;
103298348Ssgalabov                        break;
104298348Ssgalabov		}
105298348Ssgalabov        }
106298348Ssgalabov
107298348Ssgalabov	if (!found)
108298348Ssgalabov		return (ENOENT);
109298348Ssgalabov
110298348Ssgalabov        for (j = 0; j < table[i].funcnum; j++) {
111298348Ssgalabov                if (strcmp(table[i].functions[j].name, func) == 0) {
112298348Ssgalabov                        val = mtk_sysctl_get(table[i].sysc_reg);
113298348Ssgalabov                        val &= ~(table[i].mask << table[i].offset);
114298348Ssgalabov                        val |= (table[i].functions[j].value << table[i].offset);
115298348Ssgalabov                        mtk_sysctl_set(table[i].sysc_reg, val);
116298348Ssgalabov                        return (0);
117298348Ssgalabov		}
118298348Ssgalabov	}
119298348Ssgalabov
120298348Ssgalabov	return (ENOENT);
121298348Ssgalabov}
122298348Ssgalabov
123298348Ssgalabovstatic int
124298348Ssgalabovmtk_pinctrl_process_node(device_t dev, struct mtk_pin_group *table,
125298348Ssgalabov    phandle_t node)
126298348Ssgalabov{
127298348Ssgalabov	const char **group_list = NULL;
128298348Ssgalabov	char *pin_function = NULL;
129298348Ssgalabov	int ret, num_groups, i;
130298348Ssgalabov
131298348Ssgalabov	ret = 0;
132298348Ssgalabov
133298348Ssgalabov	num_groups = ofw_bus_string_list_to_array(node, "ralink,group",
134298348Ssgalabov	    &group_list);
135298348Ssgalabov
136298348Ssgalabov	if (num_groups <= 0)
137298348Ssgalabov		return (ENOENT);
138298348Ssgalabov
139298348Ssgalabov	if (OF_getprop_alloc(node, "ralink,function", sizeof(*pin_function),
140298348Ssgalabov			     (void **)&pin_function) == -1) {
141298348Ssgalabov		ret = ENOENT;
142298348Ssgalabov		goto out;
143298348Ssgalabov	}
144298348Ssgalabov
145298348Ssgalabov	for (i = 0; i < num_groups; i++) {
146298348Ssgalabov		if ((ret = mtk_pinctrl_process_entry(dev, table, group_list[i],
147298348Ssgalabov		    pin_function)) != 0)
148298348Ssgalabov			goto out;
149298348Ssgalabov	}
150298348Ssgalabov
151298348Ssgalabovout:
152299755Sgonzo	OF_prop_free(group_list);
153299755Sgonzo	OF_prop_free(pin_function);
154298348Ssgalabov	return (ret);
155298348Ssgalabov}
156298348Ssgalabov
157298348Ssgalabovstatic int
158297667Ssgalabovmtk_pinctrl_configure(device_t dev, phandle_t cfgxref)
159297667Ssgalabov{
160298348Ssgalabov	struct mtk_pin_group *pintable;
161298348Ssgalabov	phandle_t node, child;
162298348Ssgalabov	uint32_t socid;
163298348Ssgalabov	int ret;
164297667Ssgalabov
165298348Ssgalabov	node = OF_node_from_xref(cfgxref);
166298348Ssgalabov	ret = 0;
167298348Ssgalabov
168298348Ssgalabov	/* Now, get the system type, so we can get the proper GPIO mode array */
169298348Ssgalabov	socid = mtk_soc_get_socid();
170298348Ssgalabov
171298348Ssgalabov	switch (socid) {
172298348Ssgalabov	case MTK_SOC_RT3050: /* fallthrough */
173298348Ssgalabov	case MTK_SOC_RT3052:
174298348Ssgalabov	case MTK_SOC_RT3350:
175298348Ssgalabov		pintable = rt3050_pintable;
176298348Ssgalabov		break;
177298348Ssgalabov	case MTK_SOC_RT3352:
178298348Ssgalabov		pintable = rt3352_pintable;
179298348Ssgalabov		break;
180298348Ssgalabov	case MTK_SOC_RT3662: /* fallthrough */
181298348Ssgalabov	case MTK_SOC_RT3883:
182298348Ssgalabov		pintable = rt3883_pintable;
183298348Ssgalabov		break;
184298348Ssgalabov	case MTK_SOC_RT5350:
185298348Ssgalabov		pintable = rt5350_pintable;
186298348Ssgalabov		break;
187298348Ssgalabov	case MTK_SOC_MT7620A: /* fallthrough */
188298348Ssgalabov	case MTK_SOC_MT7620N:
189298348Ssgalabov		pintable = mt7620_pintable;
190298348Ssgalabov		break;
191298348Ssgalabov	case MTK_SOC_MT7628: /* fallthrough */
192298348Ssgalabov	case MTK_SOC_MT7688:
193298348Ssgalabov		pintable = mt7628_pintable;
194298348Ssgalabov		break;
195298348Ssgalabov	case MTK_SOC_MT7621:
196298348Ssgalabov		pintable = mt7621_pintable;
197298348Ssgalabov		break;
198298348Ssgalabov	default:
199298348Ssgalabov		ret = ENOENT;
200298348Ssgalabov		goto out;
201298348Ssgalabov	}
202298348Ssgalabov
203298348Ssgalabov	/*
204298348Ssgalabov	 * OpenWRT dts files have single child within the pinctrl nodes, which
205298348Ssgalabov	 * contains the 'ralink,group' and 'ralink,function' properties.
206298348Ssgalabov	 */
207298348Ssgalabov	for (child = OF_child(node); child != 0 && child != -1;
208298348Ssgalabov	    child = OF_peer(child)) {
209298348Ssgalabov		if ((ret = mtk_pinctrl_process_node(dev, pintable, child)) != 0)
210298348Ssgalabov			return (ret);
211298348Ssgalabov	}
212298348Ssgalabov
213298348Ssgalabovout:
214298348Ssgalabov	return (ret);
215297667Ssgalabov}
216297667Ssgalabov
217297667Ssgalabovstatic device_method_t mtk_pinctrl_methods[] = {
218297667Ssgalabov	DEVMETHOD(device_probe,			mtk_pinctrl_probe),
219297667Ssgalabov	DEVMETHOD(device_attach,		mtk_pinctrl_attach),
220297667Ssgalabov
221297667Ssgalabov	/* fdt_pinctrl interface */
222297667Ssgalabov	DEVMETHOD(fdt_pinctrl_configure,	mtk_pinctrl_configure),
223297667Ssgalabov
224297667Ssgalabov	DEVMETHOD_END
225297667Ssgalabov};
226297667Ssgalabov
227297667Ssgalabovstatic driver_t mtk_pinctrl_driver = {
228297667Ssgalabov	"pinctrl",
229297667Ssgalabov	mtk_pinctrl_methods,
230297667Ssgalabov	0,
231297667Ssgalabov};
232297667Ssgalabovstatic devclass_t mtk_pinctrl_devclass;
233297667Ssgalabov
234297667SsgalabovEARLY_DRIVER_MODULE(mtk_pinctrl, simplebus, mtk_pinctrl_driver,
235297667Ssgalabov    mtk_pinctrl_devclass, 0, 0, BUS_PASS_INTERRUPT + BUS_PASS_ORDER_EARLY);
236297667Ssgalabov
237297667SsgalabovMODULE_DEPEND(mtk_pinctrl, mtk_sysctl, 1, 1, 1);
238