apple_pinctrl.c revision 1.1
1/*	$NetBSD: apple_pinctrl.c,v 1.1 2022/04/27 07:59:18 skrll Exp $	*/
2/*	$OpenBSD: aplpinctrl.c,v 1.4 2022/04/06 18:59:26 naddy Exp $	*/
3
4/*-
5 * Copyright (c) 2022 The NetBSD Foundation, Inc.
6 * All rights reserved.
7 *
8 * This code is derived from software contributed to The NetBSD Foundation
9 * by Nick Hudson
10 *
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
13 * are met:
14 * 1. Redistributions of source code must retain the above copyright
15 *    notice, this list of conditions and the following disclaimer.
16 * 2. Redistributions in binary form must reproduce the above copyright
17 *    notice, this list of conditions and the following disclaimer in the
18 *    documentation and/or other materials provided with the distribution.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
21 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
22 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
23 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
24 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30 * POSSIBILITY OF SUCH DAMAGE.
31 */
32
33/*
34 * Copyright (c) 2021 Mark Kettenis <kettenis@openbsd.org>
35 *
36 * Permission to use, copy, modify, and distribute this software for any
37 * purpose with or without fee is hereby granted, provided that the above
38 * copyright notice and this permission notice appear in all copies.
39 *
40 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
41 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
42 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
43 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
44 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
45 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
46 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
47 */
48
49#include <sys/cdefs.h>
50__KERNEL_RCSID(0, "$NetBSD: apple_pinctrl.c,v 1.1 2022/04/27 07:59:18 skrll Exp $");
51
52#include <sys/param.h>
53#include <sys/bus.h>
54#include <sys/device.h>
55#include <sys/kmem.h>
56
57#include <dev/fdt/fdtvar.h>
58
59#include <arm/pic/picvar.h>
60
61#define APPLE_PIN(pinmux)	__SHIFTOUT((pinmux), __BITS(15, 0))
62#define APPLE_FUNC(pinmux)	__SHIFTOUT((pinmux), __BITS(31, 16))
63
64#define GPIO_PIN(pin)		((pin) * 4)
65#define  GPIO_PIN_GROUP_MASK	__BITS(18, 16)
66#define  GPIO_PIN_INPUT_ENABLE	__BIT(9)
67#define  GPIO_PIN_FUNC_MASK	__BITS(6, 5)
68#define  GPIO_PIN_MODE_MASK	__BITS(3, 1)
69#define  GPIO_PIN_MODE_INPUT	 __SHIFTIN(0, GPIO_PIN_MODE_MASK);
70#define  GPIO_PIN_MODE_OUTPUT	 __SHIFTIN(1, GPIO_PIN_MODE_MASK);
71#define  GPIO_PIN_MODE_IRQ_HI	 __SHIFTIN(2, GPIO_PIN_MODE_MASK);
72#define  GPIO_PIN_MODE_IRQ_LO	 __SHIFTIN(3, GPIO_PIN_MODE_MASK);
73#define  GPIO_PIN_MODE_IRQ_UP	 __SHIFTIN(4, GPIO_PIN_MODE_MASK);
74#define  GPIO_PIN_MODE_IRQ_DN	 __SHIFTIN(5, GPIO_PIN_MODE_MASK);
75#define  GPIO_PIN_MODE_IRQ_ANY	 __SHIFTIN(6, GPIO_PIN_MODE_MASK);
76#define  GPIO_PIN_MODE_IRQ_OFF	 __SHIFTIN(7, GPIO_PIN_MODE_MASK);
77#define  GPIO_PIN_DATA		__BIT(0)
78#define GPIO_IRQ(grp, pin)	(0x800 + (grp) * 64 + ((pin) >> 5) * 4)
79
80#define PINCTRL_READ(sc, reg)						\
81	(bus_space_read_4((sc)->sc_bst, (sc)->sc_bsh, (reg)))
82#define PINCTRL_WRITE(sc, reg, val)					\
83	bus_space_write_4((sc)->sc_bst, (sc)->sc_bsh, (reg), (val))
84#define PINCTRL_SET(sc, reg, bits)					\
85	PINCTRL_WRITE((sc), (reg), PINCTRL_READ((sc), (reg)) | (bits))
86#define PINCTRL_CLR(sc, reg, bits)					\
87	PINCTRL_WRITE((sc), (reg), PINCTRL_READ((sc), (reg)) & ~(bits))
88
89
90struct apple_pinctrl_softc {
91	device_t sc_dev;
92	int sc_phandle;
93	bus_space_tag_t sc_bst;
94	bus_space_handle_t sc_bsh;
95	u_int sc_npins;
96};
97
98struct apple_gpio_pin {
99	int		pin_no;
100	u_int		pin_flags;
101	bool		pin_actlo;
102};
103
104static const struct device_compatible_entry compat_data[] = {
105	{ .compat = "apple,pinctrl" },
106	DEVICE_COMPAT_EOL
107};
108
109static void
110apple_gpio_pin_ctl(void *cookie, int pin, int flags)
111{
112	struct apple_pinctrl_softc * const sc = cookie;
113
114	KASSERT(pin < sc->sc_npins);
115
116	uint32_t reg = PINCTRL_READ(sc, GPIO_PIN(pin));
117	reg &= ~GPIO_PIN_FUNC_MASK;
118	reg &= ~GPIO_PIN_MODE_MASK;
119
120	if (flags & (GPIO_PIN_OUTPUT | GPIO_PIN_INPUT)) {
121		if (flags & GPIO_PIN_INPUT) {
122			/* for safety INPUT will override output */
123			reg |= GPIO_PIN_MODE_INPUT;
124		} else {
125			reg |= GPIO_PIN_MODE_OUTPUT;
126		}
127	}
128	PINCTRL_WRITE(sc, GPIO_PIN(pin), reg);
129}
130
131static void *
132apple_pinctrl_gpio_acquire(device_t dev, const void *data, size_t len, int flags)
133{
134	struct apple_pinctrl_softc * const sc = device_private(dev);
135	struct apple_gpio_pin *pin;
136	const u_int *gpio = data;
137
138	if (len != 12)
139		return NULL;
140
141	const u_int pinno = be32toh(gpio[1]);
142	const bool actlo = be32toh(gpio[2]) & 1;
143
144	if (pinno >= sc->sc_npins)
145		return NULL;
146
147	pin = kmem_alloc(sizeof(*pin), KM_SLEEP);
148	pin->pin_no = pinno;
149	pin->pin_flags = flags;
150	pin->pin_actlo = actlo;
151
152	apple_gpio_pin_ctl(sc, pin->pin_no, pin->pin_flags);
153
154	return pin;
155}
156
157static void
158apple_pinctrl_gpio_release(device_t dev, void *priv)
159{
160	struct apple_pinctrl_softc * const sc = device_private(dev);
161	struct apple_gpio_pin *pin = priv;
162
163	apple_gpio_pin_ctl(sc, pin->pin_no, GPIO_PIN_INPUT);
164	kmem_free(pin, sizeof(*pin));
165}
166
167static int
168apple_pinctrl_gpio_read(device_t dev, void *priv, bool raw)
169{
170	struct apple_pinctrl_softc * const sc = device_private(dev);
171	struct apple_gpio_pin *pin = priv;
172
173	KASSERT(pin->pin_no < sc->sc_npins);
174
175	uint32_t reg = PINCTRL_READ(sc, GPIO_PIN(pin->pin_no));
176	int val = __SHIFTOUT(reg, GPIO_PIN_DATA);
177	if (!raw && pin->pin_actlo)
178		val = !val;
179
180	return val;
181}
182
183static void
184apple_pinctrl_gpio_write(device_t dev, void *priv, int val, bool raw)
185{
186	struct apple_pinctrl_softc * const sc = device_private(dev);
187	struct apple_gpio_pin *pin = priv;
188
189	KASSERT(pin->pin_no < sc->sc_npins);
190
191	if (!raw && pin->pin_actlo)
192		val = !val;
193
194	if (val)
195		PINCTRL_SET(sc, GPIO_PIN(pin->pin_no), GPIO_PIN_DATA);
196	else
197		PINCTRL_CLR(sc, GPIO_PIN(pin->pin_no), GPIO_PIN_DATA);
198}
199
200static int
201apple_pinctrl_set_config(device_t dev, const void *data, size_t len)
202{
203	struct apple_pinctrl_softc * const sc = device_private(dev);
204
205	if (len != 4)
206		return -1;
207
208	int pins_len;
209	const int phandle = fdtbus_get_phandle_from_native(be32dec(data));
210	const u_int *pins = fdtbus_get_prop(phandle, "pinmux", &pins_len);
211
212	if (pins == NULL)
213		return -1;
214
215	const u_int npins = pins_len / sizeof(uint32_t);
216
217	for (u_int i = 0; i < npins; i++) {
218		uint32_t pinmux = be32dec(&pins[i]);
219		u_int pinno = APPLE_PIN(pinmux);
220		u_int func = APPLE_FUNC(pinmux);
221
222		uint32_t reg = PINCTRL_READ(sc, GPIO_PIN(pinno));
223		reg &= ~GPIO_PIN_FUNC_MASK;
224		reg |= __SHIFTIN(func, GPIO_PIN_FUNC_MASK);
225
226		PINCTRL_WRITE(sc, GPIO_PIN(pinno), reg);
227	}
228
229	return 0;
230}
231
232static struct fdtbus_gpio_controller_func apple_pinctrl_gpio_funcs = {
233	.acquire = apple_pinctrl_gpio_acquire,
234	.release = apple_pinctrl_gpio_release,
235	.read = apple_pinctrl_gpio_read,
236	.write = apple_pinctrl_gpio_write
237};
238
239static struct fdtbus_pinctrl_controller_func apple_pinctrl_funcs = {
240	.set_config = apple_pinctrl_set_config,
241};
242
243static int
244apple_pinctrl_match(device_t parent, cfdata_t cf, void *aux)
245{
246	struct fdt_attach_args * const faa = aux;
247
248	return of_compatible_match(faa->faa_phandle, compat_data);
249}
250
251static void
252apple_pinctrl_attach(device_t parent, device_t self, void *aux)
253{
254	struct apple_pinctrl_softc * const sc = device_private(self);
255	struct fdt_attach_args * const faa = aux;
256	const int phandle = faa->faa_phandle;
257	bus_addr_t addr;
258	bus_size_t size;
259
260	if (fdtbus_get_reg(phandle, 0, &addr, &size) != 0) {
261		aprint_error(": couldn't get registers\n");
262		return;
263	}
264
265	sc->sc_dev = self;
266	sc->sc_bst = faa->faa_bst;
267
268	if (bus_space_map(sc->sc_bst, addr, size, 0, &sc->sc_bsh) != 0) {
269		aprint_error(": couldn't map registers\n");
270		return;
271	}
272
273	if (of_getprop_uint32(phandle, "apple,npins", &sc->sc_npins)) {
274		aprint_error(": couldn't get number of pins\n");
275		return;
276	}
277
278	if (!of_hasprop(phandle, "gpio-controller")) {
279		aprint_error(": no gpio controller");
280		return;
281	}
282
283	aprint_naive("\n");
284	aprint_normal(": Apple Pinctrl\n");
285
286	fdtbus_register_gpio_controller(self, phandle, &apple_pinctrl_gpio_funcs);
287
288	for (int child = OF_child(phandle); child; child = OF_peer(child)) {
289		if (!of_hasprop(child, "pinmux"))
290			continue;
291		fdtbus_register_pinctrl_config(self, child,
292		    &apple_pinctrl_funcs);
293
294	}
295}
296
297CFATTACH_DECL_NEW(apple_pinctrl, sizeof(struct apple_pinctrl_softc),
298    apple_pinctrl_match, apple_pinctrl_attach, NULL, NULL);
299
300