1/*-
2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3 *
4 * Copyright (c) 2017 Tom Jones <tj@enoti.me>
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 *    notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 *    notice, this list of conditions and the following disclaimer in the
14 *    documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26 * SUCH DAMAGE.
27 *
28 */
29
30/*
31 * Copyright (c) 2016 Mark Kettenis
32 *
33 * Permission to use, copy, modify, and distribute this software for any
34 * purpose with or without fee is hereby granted, provided that the above
35 * copyright notice and this permission notice appear in all copies.
36 *
37 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
38 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
39 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
40 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
41 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
42 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
43 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
44 */
45#include <sys/cdefs.h>
46__FBSDID("$FreeBSD$");
47
48#include <sys/param.h>
49#include <sys/systm.h>
50#include <sys/bus.h>
51#include <sys/gpio.h>
52#include <sys/clock.h>
53#include <sys/kernel.h>
54#include <sys/module.h>
55#include <sys/endian.h>
56#include <sys/rman.h>
57#include <sys/types.h>
58#include <sys/malloc.h>
59
60#include <machine/bus.h>
61#include <machine/resource.h>
62
63#include <contrib/dev/acpica/include/acpi.h>
64#include <contrib/dev/acpica/include/accommon.h>
65
66#include <dev/acpica/acpivar.h>
67#include <dev/gpio/gpiobusvar.h>
68
69#include "opt_platform.h"
70#include "opt_acpi.h"
71#include "gpio_if.h"
72
73#include "chvgpio_reg.h"
74
75/*
76 *     Macros for driver mutex locking
77 */
78#define CHVGPIO_LOCK(_sc)               mtx_lock_spin(&(_sc)->sc_mtx)
79#define CHVGPIO_UNLOCK(_sc)             mtx_unlock_spin(&(_sc)->sc_mtx)
80#define CHVGPIO_LOCK_INIT(_sc) \
81	mtx_init(&_sc->sc_mtx, device_get_nameunit((_sc)->sc_dev), \
82	"chvgpio", MTX_SPIN)
83#define CHVGPIO_LOCK_DESTROY(_sc)       mtx_destroy(&(_sc)->sc_mtx)
84#define CHVGPIO_ASSERT_LOCKED(_sc)      mtx_assert(&(_sc)->sc_mtx, MA_OWNED)
85#define CHVGPIO_ASSERT_UNLOCKED(_sc) 	mtx_assert(&(_sc)->sc_mtx, MA_NOTOWNED)
86
87struct chvgpio_softc {
88	device_t 	sc_dev;
89	device_t 	sc_busdev;
90	struct mtx 	sc_mtx;
91
92	ACPI_HANDLE	sc_handle;
93
94	int		sc_mem_rid;
95	struct resource *sc_mem_res;
96
97	int		sc_irq_rid;
98	struct resource *sc_irq_res;
99	void		*intr_handle;
100
101	const char	*sc_bank_prefix;
102	const int  	*sc_pins;
103	int 		sc_npins;
104	int 		sc_ngroups;
105	const char **sc_pin_names;
106};
107
108static void chvgpio_intr(void *);
109static int chvgpio_probe(device_t);
110static int chvgpio_attach(device_t);
111static int chvgpio_detach(device_t);
112
113static inline int
114chvgpio_pad_cfg0_offset(int pin)
115{
116	return (CHVGPIO_PAD_CFG0 + 1024 * (pin / 15) + 8 * (pin % 15));
117}
118
119static inline int
120chvgpio_read_pad_cfg0(struct chvgpio_softc *sc, int pin)
121{
122	return bus_read_4(sc->sc_mem_res, chvgpio_pad_cfg0_offset(pin));
123}
124
125static inline void
126chvgpio_write_pad_cfg0(struct chvgpio_softc *sc, int pin, uint32_t val)
127{
128	bus_write_4(sc->sc_mem_res, chvgpio_pad_cfg0_offset(pin), val);
129}
130
131static inline int
132chvgpio_read_pad_cfg1(struct chvgpio_softc *sc, int pin)
133{
134	return bus_read_4(sc->sc_mem_res, chvgpio_pad_cfg0_offset(pin) + 4);
135}
136
137static device_t
138chvgpio_get_bus(device_t dev)
139{
140	struct chvgpio_softc *sc;
141
142	sc = device_get_softc(dev);
143
144	return (sc->sc_busdev);
145}
146
147static int
148chvgpio_pin_max(device_t dev, int *maxpin)
149{
150	struct chvgpio_softc *sc;
151
152	sc = device_get_softc(dev);
153
154	*maxpin = sc->sc_npins - 1;
155
156	return (0);
157}
158
159static int
160chvgpio_valid_pin(struct chvgpio_softc *sc, int pin)
161{
162	if (pin < 0)
163		return EINVAL;
164	if ((pin / 15) >= sc->sc_ngroups)
165		return EINVAL;
166	if ((pin % 15) >= sc->sc_pins[pin / 15])
167		return EINVAL;
168	return (0);
169}
170
171static int
172chvgpio_pin_getname(device_t dev, uint32_t pin, char *name)
173{
174	struct chvgpio_softc *sc;
175
176	sc = device_get_softc(dev);
177	if (chvgpio_valid_pin(sc, pin) != 0)
178		return (EINVAL);
179
180	/* return pin name from datasheet */
181	snprintf(name, GPIOMAXNAME, "%s", sc->sc_pin_names[pin]);
182	name[GPIOMAXNAME - 1] = '\0';
183	return (0);
184}
185
186static int
187chvgpio_pin_getcaps(device_t dev, uint32_t pin, uint32_t *caps)
188{
189	struct chvgpio_softc *sc;
190
191	sc = device_get_softc(dev);
192	if (chvgpio_valid_pin(sc, pin) != 0)
193		return (EINVAL);
194
195	*caps = 0;
196	if (chvgpio_valid_pin(sc, pin))
197		*caps = GPIO_PIN_INPUT | GPIO_PIN_OUTPUT;
198
199	return (0);
200}
201
202static int
203chvgpio_pin_getflags(device_t dev, uint32_t pin, uint32_t *flags)
204{
205	struct chvgpio_softc *sc;
206	uint32_t val;
207
208	sc = device_get_softc(dev);
209	if (chvgpio_valid_pin(sc, pin) != 0)
210		return (EINVAL);
211
212	*flags = 0;
213
214	/* Get the current pin state */
215	CHVGPIO_LOCK(sc);
216	val = chvgpio_read_pad_cfg0(sc, pin);
217
218	if (val & CHVGPIO_PAD_CFG0_GPIOCFG_GPIO ||
219		val & CHVGPIO_PAD_CFG0_GPIOCFG_GPO)
220		*flags |= GPIO_PIN_OUTPUT;
221
222	if (val & CHVGPIO_PAD_CFG0_GPIOCFG_GPIO ||
223		val & CHVGPIO_PAD_CFG0_GPIOCFG_GPI)
224		*flags |= GPIO_PIN_INPUT;
225
226	val = chvgpio_read_pad_cfg1(sc, pin);
227
228	CHVGPIO_UNLOCK(sc);
229	return (0);
230}
231
232static int
233chvgpio_pin_setflags(device_t dev, uint32_t pin, uint32_t flags)
234{
235	struct chvgpio_softc *sc;
236	uint32_t val;
237	uint32_t allowed;
238
239	sc = device_get_softc(dev);
240	if (chvgpio_valid_pin(sc, pin) != 0)
241		return (EINVAL);
242
243	allowed = GPIO_PIN_INPUT | GPIO_PIN_OUTPUT;
244
245	/*
246	 * Only direction flag allowed
247	 */
248	if (flags & ~allowed)
249		return (EINVAL);
250
251	/*
252	 * Not both directions simultaneously
253	 */
254	if ((flags & allowed) == allowed)
255		return (EINVAL);
256
257	/* Set the GPIO mode and state */
258	CHVGPIO_LOCK(sc);
259	val = chvgpio_read_pad_cfg0(sc, pin);
260	if (flags & GPIO_PIN_INPUT)
261		val = val & CHVGPIO_PAD_CFG0_GPIOCFG_GPI;
262	if (flags & GPIO_PIN_OUTPUT)
263		val = val & CHVGPIO_PAD_CFG0_GPIOCFG_GPO;
264	chvgpio_write_pad_cfg0(sc, pin, val);
265	CHVGPIO_UNLOCK(sc);
266
267	return (0);
268}
269
270static int
271chvgpio_pin_set(device_t dev, uint32_t pin, unsigned int value)
272{
273	struct chvgpio_softc *sc;
274	uint32_t val;
275
276	sc = device_get_softc(dev);
277	if (chvgpio_valid_pin(sc, pin) != 0)
278		return (EINVAL);
279
280	CHVGPIO_LOCK(sc);
281	val = chvgpio_read_pad_cfg0(sc, pin);
282	if (value == GPIO_PIN_LOW)
283		val = val & ~CHVGPIO_PAD_CFG0_GPIOTXSTATE;
284	else
285		val = val | CHVGPIO_PAD_CFG0_GPIOTXSTATE;
286	chvgpio_write_pad_cfg0(sc, pin, val);
287	CHVGPIO_UNLOCK(sc);
288
289	return (0);
290}
291
292static int
293chvgpio_pin_get(device_t dev, uint32_t pin, unsigned int *value)
294{
295	struct chvgpio_softc *sc;
296	uint32_t val;
297
298	sc = device_get_softc(dev);
299	if (chvgpio_valid_pin(sc, pin) != 0)
300		return (EINVAL);
301
302	CHVGPIO_LOCK(sc);
303
304	/* Read pin value */
305	val = chvgpio_read_pad_cfg0(sc, pin);
306	if (val & CHVGPIO_PAD_CFG0_GPIORXSTATE)
307		*value = GPIO_PIN_HIGH;
308	else
309		*value = GPIO_PIN_LOW;
310
311	CHVGPIO_UNLOCK(sc);
312
313	return (0);
314}
315
316static int
317chvgpio_pin_toggle(device_t dev, uint32_t pin)
318{
319	struct chvgpio_softc *sc;
320	uint32_t val;
321
322	sc = device_get_softc(dev);
323	if (chvgpio_valid_pin(sc, pin) != 0)
324		return (EINVAL);
325
326	CHVGPIO_LOCK(sc);
327
328	/* Toggle the pin */
329	val = chvgpio_read_pad_cfg0(sc, pin);
330	val = val ^ CHVGPIO_PAD_CFG0_GPIOTXSTATE;
331	chvgpio_write_pad_cfg0(sc, pin, val);
332
333	CHVGPIO_UNLOCK(sc);
334
335	return (0);
336}
337
338static char *chvgpio_hids[] = {
339	"INT33FF",
340	NULL
341};
342
343static int
344chvgpio_probe(device_t dev)
345{
346    if (acpi_disabled("chvgpio") ||
347    ACPI_ID_PROBE(device_get_parent(dev), dev, chvgpio_hids) == NULL)
348        return (ENXIO);
349
350    device_set_desc(dev, "Intel Cherry View GPIO");
351    return (0);
352}
353
354static int
355chvgpio_attach(device_t dev)
356{
357	struct chvgpio_softc *sc;
358	ACPI_STATUS status;
359	int uid;
360	int i;
361	int error;
362
363	sc = device_get_softc(dev);
364	sc->sc_dev = dev;
365	sc->sc_handle = acpi_get_handle(dev);
366
367	status = acpi_GetInteger(sc->sc_handle, "_UID", &uid);
368	if (ACPI_FAILURE(status)) {
369		device_printf(dev, "failed to read _UID\n");
370		return (ENXIO);
371	}
372
373	CHVGPIO_LOCK_INIT(sc);
374
375	switch (uid) {
376	case SW_UID:
377		sc->sc_bank_prefix = SW_BANK_PREFIX;
378		sc->sc_pins = chv_southwest_pins;
379		sc->sc_pin_names = chv_southwest_pin_names;
380		break;
381	case N_UID:
382		sc->sc_bank_prefix = N_BANK_PREFIX;
383		sc->sc_pins = chv_north_pins;
384		sc->sc_pin_names = chv_north_pin_names;
385		break;
386	case E_UID:
387		sc->sc_bank_prefix = E_BANK_PREFIX;
388		sc->sc_pins = chv_east_pins;
389		sc->sc_pin_names = chv_east_pin_names;
390		break;
391	case SE_UID:
392		sc->sc_bank_prefix = SE_BANK_PREFIX;
393		sc->sc_pins = chv_southeast_pins;
394		sc->sc_pin_names = chv_southeast_pin_names;
395		break;
396	default:
397		device_printf(dev, "invalid _UID value: %d\n", uid);
398		return (ENXIO);
399	}
400
401	for (i = 0; sc->sc_pins[i] >= 0; i++) {
402		sc->sc_npins += sc->sc_pins[i];
403		sc->sc_ngroups++;
404	}
405
406	sc->sc_mem_rid = 0;
407	sc->sc_mem_res = bus_alloc_resource_any(sc->sc_dev, SYS_RES_MEMORY,
408		&sc->sc_mem_rid, RF_ACTIVE);
409	if (sc->sc_mem_res == NULL) {
410		CHVGPIO_LOCK_DESTROY(sc);
411		device_printf(dev, "can't allocate memory resource\n");
412		return (ENOMEM);
413	}
414
415	sc->sc_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ,
416		&sc->sc_irq_rid, RF_ACTIVE);
417
418	if (!sc->sc_irq_res) {
419		CHVGPIO_LOCK_DESTROY(sc);
420		bus_release_resource(dev, SYS_RES_MEMORY,
421			sc->sc_mem_rid, sc->sc_mem_res);
422		device_printf(dev, "can't allocate irq resource\n");
423		return (ENOMEM);
424	}
425
426	error = bus_setup_intr(sc->sc_dev, sc->sc_irq_res, INTR_TYPE_MISC | INTR_MPSAFE,
427		NULL, chvgpio_intr, sc, &sc->intr_handle);
428
429
430	if (error) {
431		device_printf(sc->sc_dev, "unable to setup irq: error %d\n", error);
432		CHVGPIO_LOCK_DESTROY(sc);
433		bus_release_resource(dev, SYS_RES_MEMORY,
434			sc->sc_mem_rid, sc->sc_mem_res);
435		bus_release_resource(dev, SYS_RES_IRQ,
436			sc->sc_irq_rid, sc->sc_irq_res);
437		return (ENXIO);
438	}
439
440	/* Mask and ack all interrupts. */
441	bus_write_4(sc->sc_mem_res, CHVGPIO_INTERRUPT_MASK, 0);
442	bus_write_4(sc->sc_mem_res, CHVGPIO_INTERRUPT_STATUS, 0xffff);
443
444	sc->sc_busdev = gpiobus_attach_bus(dev);
445	if (sc->sc_busdev == NULL) {
446		CHVGPIO_LOCK_DESTROY(sc);
447		bus_release_resource(dev, SYS_RES_MEMORY,
448			sc->sc_mem_rid, sc->sc_mem_res);
449		bus_release_resource(dev, SYS_RES_IRQ,
450			sc->sc_irq_rid, sc->sc_irq_res);
451		return (ENXIO);
452	}
453
454	return (0);
455}
456
457static void
458chvgpio_intr(void *arg)
459{
460	struct chvgpio_softc *sc = arg;
461	uint32_t reg;
462	int line;
463
464	reg = bus_read_4(sc->sc_mem_res, CHVGPIO_INTERRUPT_STATUS);
465	for (line = 0; line < 16; line++) {
466		if ((reg & (1 << line)) == 0)
467			continue;
468		bus_write_4(sc->sc_mem_res, CHVGPIO_INTERRUPT_STATUS, 1 << line);
469	}
470}
471
472static int
473chvgpio_detach(device_t dev)
474{
475	struct chvgpio_softc *sc;
476	sc = device_get_softc(dev);
477
478	if (sc->sc_busdev)
479		gpiobus_detach_bus(dev);
480
481	if (sc->intr_handle != NULL)
482	    bus_teardown_intr(sc->sc_dev, sc->sc_irq_res, sc->intr_handle);
483	if (sc->sc_irq_res != NULL)
484		bus_release_resource(dev, SYS_RES_IRQ, sc->sc_irq_rid, sc->sc_irq_res);
485	if (sc->sc_mem_res != NULL)
486		bus_release_resource(dev, SYS_RES_MEMORY, sc->sc_mem_rid, sc->sc_mem_res);
487
488	CHVGPIO_LOCK_DESTROY(sc);
489
490    return (0);
491}
492
493static device_method_t chvgpio_methods[] = {
494	DEVMETHOD(device_probe,     	chvgpio_probe),
495	DEVMETHOD(device_attach,    	chvgpio_attach),
496	DEVMETHOD(device_detach,    	chvgpio_detach),
497
498	/* GPIO protocol */
499	DEVMETHOD(gpio_get_bus, 	chvgpio_get_bus),
500	DEVMETHOD(gpio_pin_max, 	chvgpio_pin_max),
501	DEVMETHOD(gpio_pin_getname, 	chvgpio_pin_getname),
502	DEVMETHOD(gpio_pin_getflags,	chvgpio_pin_getflags),
503	DEVMETHOD(gpio_pin_getcaps, 	chvgpio_pin_getcaps),
504	DEVMETHOD(gpio_pin_setflags,	chvgpio_pin_setflags),
505	DEVMETHOD(gpio_pin_get, 	chvgpio_pin_get),
506	DEVMETHOD(gpio_pin_set, 	chvgpio_pin_set),
507	DEVMETHOD(gpio_pin_toggle, 	chvgpio_pin_toggle),
508
509	DEVMETHOD_END
510};
511
512static driver_t chvgpio_driver = {
513    .name = "gpio",
514    .methods = chvgpio_methods,
515    .size = sizeof(struct chvgpio_softc)
516};
517
518static devclass_t chvgpio_devclass;
519DRIVER_MODULE(chvgpio, acpi, chvgpio_driver, chvgpio_devclass, NULL , NULL);
520MODULE_DEPEND(chvgpio, acpi, 1, 1, 1);
521MODULE_DEPEND(chvgpio, gpiobus, 1, 1, 1);
522
523MODULE_VERSION(chvgpio, 1);
524