1/*-
2 * Copyright (c) 2016 Stanislav Galabov
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 *    without modification, immediately at the beginning of the file.
11 * 2. The name of the author may not be used to endorse or promote products
12 *    derived from this software without specific prior written permission.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
18 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, 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 */
27
28#include <sys/cdefs.h>
29__FBSDID("$FreeBSD$");
30
31#include <sys/param.h>
32#include <sys/kernel.h>
33#include <sys/bus.h>
34#include <sys/module.h>
35
36#include <dev/fdt/fdt_common.h>
37#include <dev/ofw/openfirm.h>
38#include <dev/ofw/ofw_bus.h>
39#include <dev/ofw/ofw_bus_subr.h>
40
41#include <dev/fdt/fdt_pinctrl.h>
42#include <mips/mediatek/mtk_sysctl.h>
43#include <mips/mediatek/mtk_soc.h>
44#include <mips/mediatek/mtk_pinctrl.h>
45
46#include "fdt_pinctrl_if.h"
47
48static const struct ofw_compat_data compat_data[] = {
49	{ "ralink,rt2880-pinmux",	1 },
50
51	/* Sentinel */
52	{ NULL,				0 }
53};
54
55static int
56mtk_pinctrl_probe(device_t dev)
57{
58
59	if (!ofw_bus_status_okay(dev))
60		return (ENXIO);
61
62	if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0)
63		return (ENXIO);
64
65	device_set_desc(dev, "MTK Pin Controller");
66
67	return (0);
68}
69
70static int
71mtk_pinctrl_attach(device_t dev)
72{
73
74	if (device_get_unit(dev) != 0) {
75		device_printf(dev, "Only one pin control allowed\n");
76		return (ENXIO);
77	}
78
79	if (bootverbose)
80		device_printf(dev, "GPIO mode start: 0x%08x\n",
81		    mtk_sysctl_get(SYSCTL_GPIOMODE));
82
83	fdt_pinctrl_register(dev, NULL);
84	fdt_pinctrl_configure_tree(dev);
85
86	if (bootverbose)
87		device_printf(dev, "GPIO mode end  : 0x%08x\n",
88		    mtk_sysctl_get(SYSCTL_GPIOMODE));
89
90	return (0);
91}
92
93static int
94mtk_pinctrl_process_entry(device_t dev, struct mtk_pin_group *table,
95    const char *group, char *func)
96{
97	uint32_t val;
98	int found = 0, i, j;
99
100	for (i = 0; table[i].name != NULL; i++) {
101                if (strcmp(table[i].name, group) == 0) {
102			found = 1;
103                        break;
104		}
105        }
106
107	if (!found)
108		return (ENOENT);
109
110        for (j = 0; j < table[i].funcnum; j++) {
111                if (strcmp(table[i].functions[j].name, func) == 0) {
112                        val = mtk_sysctl_get(table[i].sysc_reg);
113                        val &= ~(table[i].mask << table[i].offset);
114                        val |= (table[i].functions[j].value << table[i].offset);
115                        mtk_sysctl_set(table[i].sysc_reg, val);
116                        return (0);
117		}
118	}
119
120	return (ENOENT);
121}
122
123static int
124mtk_pinctrl_process_node(device_t dev, struct mtk_pin_group *table,
125    phandle_t node)
126{
127	const char **group_list = NULL;
128	char *pin_function = NULL;
129	int ret, num_groups, i;
130
131	ret = 0;
132
133	num_groups = ofw_bus_string_list_to_array(node, "ralink,group",
134	    &group_list);
135
136	if (num_groups <= 0)
137		return (ENOENT);
138
139	if (OF_getprop_alloc(node, "ralink,function", sizeof(*pin_function),
140			     (void **)&pin_function) == -1) {
141		ret = ENOENT;
142		goto out;
143	}
144
145	for (i = 0; i < num_groups; i++) {
146		if ((ret = mtk_pinctrl_process_entry(dev, table, group_list[i],
147		    pin_function)) != 0)
148			goto out;
149	}
150
151out:
152	OF_prop_free(group_list);
153	OF_prop_free(pin_function);
154	return (ret);
155}
156
157static int
158mtk_pinctrl_configure(device_t dev, phandle_t cfgxref)
159{
160	struct mtk_pin_group *pintable;
161	phandle_t node, child;
162	uint32_t socid;
163	int ret;
164
165	node = OF_node_from_xref(cfgxref);
166	ret = 0;
167
168	/* Now, get the system type, so we can get the proper GPIO mode array */
169	socid = mtk_soc_get_socid();
170
171	switch (socid) {
172	case MTK_SOC_RT3050: /* fallthrough */
173	case MTK_SOC_RT3052:
174	case MTK_SOC_RT3350:
175		pintable = rt3050_pintable;
176		break;
177	case MTK_SOC_RT3352:
178		pintable = rt3352_pintable;
179		break;
180	case MTK_SOC_RT3662: /* fallthrough */
181	case MTK_SOC_RT3883:
182		pintable = rt3883_pintable;
183		break;
184	case MTK_SOC_RT5350:
185		pintable = rt5350_pintable;
186		break;
187	case MTK_SOC_MT7620A: /* fallthrough */
188	case MTK_SOC_MT7620N:
189		pintable = mt7620_pintable;
190		break;
191	case MTK_SOC_MT7628: /* fallthrough */
192	case MTK_SOC_MT7688:
193		pintable = mt7628_pintable;
194		break;
195	case MTK_SOC_MT7621:
196		pintable = mt7621_pintable;
197		break;
198	default:
199		ret = ENOENT;
200		goto out;
201	}
202
203	/*
204	 * OpenWRT dts files have single child within the pinctrl nodes, which
205	 * contains the 'ralink,group' and 'ralink,function' properties.
206	 */
207	for (child = OF_child(node); child != 0 && child != -1;
208	    child = OF_peer(child)) {
209		if ((ret = mtk_pinctrl_process_node(dev, pintable, child)) != 0)
210			return (ret);
211	}
212
213out:
214	return (ret);
215}
216
217static device_method_t mtk_pinctrl_methods[] = {
218	DEVMETHOD(device_probe,			mtk_pinctrl_probe),
219	DEVMETHOD(device_attach,		mtk_pinctrl_attach),
220
221	/* fdt_pinctrl interface */
222	DEVMETHOD(fdt_pinctrl_configure,	mtk_pinctrl_configure),
223
224	DEVMETHOD_END
225};
226
227static driver_t mtk_pinctrl_driver = {
228	"pinctrl",
229	mtk_pinctrl_methods,
230	0,
231};
232static devclass_t mtk_pinctrl_devclass;
233
234EARLY_DRIVER_MODULE(mtk_pinctrl, simplebus, mtk_pinctrl_driver,
235    mtk_pinctrl_devclass, 0, 0, BUS_PASS_INTERRUPT + BUS_PASS_ORDER_EARLY);
236
237MODULE_DEPEND(mtk_pinctrl, mtk_sysctl, 1, 1, 1);
238