2013-02-05 02:25:13 +00:00
|
|
|
/*-
|
2017-11-27 15:04:10 +00:00
|
|
|
* SPDX-License-Identifier: BSD-2-Clause-FreeBSD
|
|
|
|
*
|
2014-03-25 08:31:47 +00:00
|
|
|
* Copyright (c) 2013 Ganbold Tsagaankhuu <ganbold@freebsd.org>
|
2013-02-05 02:25:13 +00:00
|
|
|
* Copyright (c) 2012 Oleksandr Tymoshenko <gonzo@freebsd.org>
|
|
|
|
* Copyright (c) 2012 Luiz Otavio O Souza.
|
|
|
|
* All rights reserved.
|
|
|
|
*
|
|
|
|
* Redistribution and use in source and binary forms, with or without
|
|
|
|
* modification, are permitted provided that the following conditions
|
|
|
|
* are met:
|
|
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
|
|
* notice, this list of conditions and the following disclaimer.
|
|
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
|
|
* documentation and/or other materials provided with the distribution.
|
|
|
|
*
|
|
|
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
|
|
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
|
|
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
|
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
|
|
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
|
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
|
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
|
|
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
|
|
|
* SUCH DAMAGE.
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
#include <sys/cdefs.h>
|
|
|
|
__FBSDID("$FreeBSD$");
|
|
|
|
|
|
|
|
#include <sys/param.h>
|
|
|
|
#include <sys/systm.h>
|
|
|
|
#include <sys/bus.h>
|
|
|
|
|
|
|
|
#include <sys/kernel.h>
|
|
|
|
#include <sys/module.h>
|
|
|
|
#include <sys/rman.h>
|
|
|
|
#include <sys/lock.h>
|
|
|
|
#include <sys/mutex.h>
|
|
|
|
#include <sys/gpio.h>
|
2020-01-19 19:51:20 +00:00
|
|
|
#include <sys/proc.h>
|
2013-02-05 02:25:13 +00:00
|
|
|
|
|
|
|
#include <machine/bus.h>
|
|
|
|
#include <machine/resource.h>
|
|
|
|
#include <machine/intr.h>
|
|
|
|
|
2015-01-31 19:32:14 +00:00
|
|
|
#include <dev/gpio/gpiobusvar.h>
|
2013-02-05 02:25:13 +00:00
|
|
|
#include <dev/ofw/ofw_bus.h>
|
|
|
|
#include <dev/ofw/ofw_bus_subr.h>
|
2016-11-14 11:52:22 +00:00
|
|
|
#include <dev/fdt/fdt_pinctrl.h>
|
2013-02-05 02:25:13 +00:00
|
|
|
|
2016-08-17 21:44:02 +00:00
|
|
|
#include <arm/allwinner/aw_machdep.h>
|
2016-02-17 18:28:03 +00:00
|
|
|
#include <arm/allwinner/allwinner_pinctrl.h>
|
2016-04-23 13:59:18 +00:00
|
|
|
#include <dev/extres/clk/clk.h>
|
|
|
|
#include <dev/extres/hwreset/hwreset.h>
|
2020-01-16 20:02:41 +00:00
|
|
|
#include <dev/extres/regulator/regulator.h>
|
2016-02-17 18:28:03 +00:00
|
|
|
|
2016-07-11 20:09:17 +00:00
|
|
|
#if defined(__aarch64__)
|
|
|
|
#include "opt_soc.h"
|
|
|
|
#endif
|
|
|
|
|
2020-01-19 19:51:20 +00:00
|
|
|
#include "pic_if.h"
|
2013-02-05 02:25:13 +00:00
|
|
|
#include "gpio_if.h"
|
|
|
|
|
2017-12-26 12:11:04 +00:00
|
|
|
#define AW_GPIO_DEFAULT_CAPS (GPIO_PIN_INPUT | GPIO_PIN_OUTPUT | \
|
2020-01-19 19:51:20 +00:00
|
|
|
GPIO_PIN_PULLUP | GPIO_PIN_PULLDOWN);
|
|
|
|
|
|
|
|
#define AW_GPIO_INTR_CAPS (GPIO_INTR_LEVEL_LOW | GPIO_INTR_LEVEL_HIGH | \
|
|
|
|
GPIO_INTR_EDGE_RISING | GPIO_INTR_EDGE_FALLING | GPIO_INTR_EDGE_BOTH)
|
2013-02-05 02:25:13 +00:00
|
|
|
|
2017-12-26 12:11:04 +00:00
|
|
|
#define AW_GPIO_NONE 0
|
|
|
|
#define AW_GPIO_PULLUP 1
|
|
|
|
#define AW_GPIO_PULLDOWN 2
|
2013-02-19 02:01:35 +00:00
|
|
|
|
2017-12-26 12:11:04 +00:00
|
|
|
#define AW_GPIO_INPUT 0
|
|
|
|
#define AW_GPIO_OUTPUT 1
|
2013-02-19 02:01:35 +00:00
|
|
|
|
2016-04-23 13:59:18 +00:00
|
|
|
#define AW_GPIO_DRV_MASK 0x3
|
|
|
|
#define AW_GPIO_PUD_MASK 0x3
|
2016-02-17 18:28:03 +00:00
|
|
|
|
2016-04-23 13:59:18 +00:00
|
|
|
#define AW_PINCTRL 1
|
|
|
|
#define AW_R_PINCTRL 2
|
2013-02-05 02:25:13 +00:00
|
|
|
|
2020-01-16 20:02:41 +00:00
|
|
|
struct aw_gpio_conf {
|
|
|
|
struct allwinner_padconf *padconf;
|
|
|
|
const char *banks;
|
|
|
|
};
|
|
|
|
|
2017-12-26 12:11:04 +00:00
|
|
|
/* Defined in aw_padconf.c */
|
2016-02-17 18:28:03 +00:00
|
|
|
#ifdef SOC_ALLWINNER_A10
|
2020-01-16 20:02:41 +00:00
|
|
|
extern struct allwinner_padconf a10_padconf;
|
|
|
|
struct aw_gpio_conf a10_gpio_conf = {
|
|
|
|
.padconf = &a10_padconf,
|
|
|
|
.banks = "abcdefghi",
|
|
|
|
};
|
2016-02-17 18:28:03 +00:00
|
|
|
#endif
|
|
|
|
|
2016-07-08 23:38:25 +00:00
|
|
|
/* Defined in a13_padconf.c */
|
|
|
|
#ifdef SOC_ALLWINNER_A13
|
2020-01-16 20:02:41 +00:00
|
|
|
extern struct allwinner_padconf a13_padconf;
|
|
|
|
struct aw_gpio_conf a13_gpio_conf = {
|
|
|
|
.padconf = &a13_padconf,
|
|
|
|
.banks = "bcdefg",
|
|
|
|
};
|
2016-07-08 23:38:25 +00:00
|
|
|
#endif
|
|
|
|
|
2016-02-17 18:28:03 +00:00
|
|
|
/* Defined in a20_padconf.c */
|
|
|
|
#ifdef SOC_ALLWINNER_A20
|
2020-01-16 20:02:41 +00:00
|
|
|
extern struct allwinner_padconf a20_padconf;
|
|
|
|
struct aw_gpio_conf a20_gpio_conf = {
|
|
|
|
.padconf = &a20_padconf,
|
|
|
|
.banks = "abcdefghi",
|
|
|
|
};
|
2016-02-17 18:28:03 +00:00
|
|
|
#endif
|
|
|
|
|
2016-02-25 12:17:41 +00:00
|
|
|
/* Defined in a31_padconf.c */
|
|
|
|
#ifdef SOC_ALLWINNER_A31
|
2020-01-16 20:02:41 +00:00
|
|
|
extern struct allwinner_padconf a31_padconf;
|
|
|
|
struct aw_gpio_conf a31_gpio_conf = {
|
|
|
|
.padconf = &a31_padconf,
|
|
|
|
.banks = "abcdefgh",
|
|
|
|
};
|
2016-02-25 12:17:41 +00:00
|
|
|
#endif
|
|
|
|
|
|
|
|
/* Defined in a31s_padconf.c */
|
|
|
|
#ifdef SOC_ALLWINNER_A31S
|
2020-01-16 20:02:41 +00:00
|
|
|
extern struct allwinner_padconf a31s_padconf;
|
|
|
|
struct aw_gpio_conf a31s_gpio_conf = {
|
|
|
|
.padconf = &a31s_padconf,
|
|
|
|
.banks = "abcdefgh",
|
|
|
|
};
|
2016-02-25 12:17:41 +00:00
|
|
|
#endif
|
|
|
|
|
2016-04-23 13:59:18 +00:00
|
|
|
#if defined(SOC_ALLWINNER_A31) || defined(SOC_ALLWINNER_A31S)
|
2020-01-16 20:02:41 +00:00
|
|
|
extern struct allwinner_padconf a31_r_padconf;
|
|
|
|
struct aw_gpio_conf a31_r_gpio_conf = {
|
|
|
|
.padconf = &a31_r_padconf,
|
|
|
|
.banks = "lm",
|
|
|
|
};
|
2016-04-23 13:59:18 +00:00
|
|
|
#endif
|
|
|
|
|
2017-01-04 03:35:39 +00:00
|
|
|
/* Defined in a33_padconf.c */
|
|
|
|
#ifdef SOC_ALLWINNER_A33
|
2020-01-16 20:02:41 +00:00
|
|
|
extern struct allwinner_padconf a33_padconf;
|
|
|
|
struct aw_gpio_conf a33_gpio_conf = {
|
|
|
|
.padconf = &a33_padconf,
|
|
|
|
.banks = "bcdefgh",
|
|
|
|
};
|
2017-01-04 03:35:39 +00:00
|
|
|
#endif
|
|
|
|
|
2016-05-13 18:20:54 +00:00
|
|
|
/* Defined in h3_padconf.c */
|
2017-12-05 21:21:23 +00:00
|
|
|
#if defined(SOC_ALLWINNER_H3) || defined(SOC_ALLWINNER_H5)
|
2020-01-16 20:02:41 +00:00
|
|
|
extern struct allwinner_padconf h3_padconf;
|
|
|
|
extern struct allwinner_padconf h3_r_padconf;
|
|
|
|
struct aw_gpio_conf h3_gpio_conf = {
|
|
|
|
.padconf = &h3_padconf,
|
|
|
|
.banks = "acdefg",
|
|
|
|
};
|
|
|
|
struct aw_gpio_conf h3_r_gpio_conf = {
|
|
|
|
.padconf = &h3_r_padconf,
|
|
|
|
.banks = "l",
|
|
|
|
};
|
2016-05-13 18:20:54 +00:00
|
|
|
#endif
|
|
|
|
|
2016-05-05 09:41:57 +00:00
|
|
|
/* Defined in a83t_padconf.c */
|
|
|
|
#ifdef SOC_ALLWINNER_A83T
|
2020-01-16 20:02:41 +00:00
|
|
|
extern struct allwinner_padconf a83t_padconf;
|
|
|
|
extern struct allwinner_padconf a83t_r_padconf;
|
|
|
|
struct aw_gpio_conf a83t_gpio_conf = {
|
|
|
|
.padconf = &a83t_padconf,
|
|
|
|
.banks = "bcdefgh"
|
|
|
|
};
|
|
|
|
struct aw_gpio_conf a83t_r_gpio_conf = {
|
|
|
|
.padconf = &a83t_r_padconf,
|
|
|
|
.banks = "l",
|
|
|
|
};
|
2016-05-05 09:41:57 +00:00
|
|
|
#endif
|
|
|
|
|
2016-07-11 20:09:17 +00:00
|
|
|
/* Defined in a64_padconf.c */
|
|
|
|
#ifdef SOC_ALLWINNER_A64
|
2020-01-16 20:02:41 +00:00
|
|
|
extern struct allwinner_padconf a64_padconf;
|
|
|
|
extern struct allwinner_padconf a64_r_padconf;
|
|
|
|
struct aw_gpio_conf a64_gpio_conf = {
|
|
|
|
.padconf = &a64_padconf,
|
|
|
|
.banks = "bcdefgh",
|
|
|
|
};
|
|
|
|
struct aw_gpio_conf a64_r_gpio_conf = {
|
|
|
|
.padconf = &a64_r_padconf,
|
|
|
|
.banks = "l",
|
|
|
|
};
|
2016-07-11 20:09:17 +00:00
|
|
|
#endif
|
|
|
|
|
2019-10-14 21:55:45 +00:00
|
|
|
/* Defined in h6_padconf.c */
|
|
|
|
#ifdef SOC_ALLWINNER_H6
|
2020-01-16 20:02:41 +00:00
|
|
|
extern struct allwinner_padconf h6_padconf;
|
|
|
|
extern struct allwinner_padconf h6_r_padconf;
|
|
|
|
struct aw_gpio_conf h6_gpio_conf = {
|
|
|
|
.padconf = &h6_padconf,
|
|
|
|
.banks = "cdfgh",
|
|
|
|
};
|
|
|
|
struct aw_gpio_conf h6_r_gpio_conf = {
|
|
|
|
.padconf = &h6_r_padconf,
|
|
|
|
.banks = "lm",
|
|
|
|
};
|
2019-10-14 21:55:45 +00:00
|
|
|
#endif
|
|
|
|
|
2016-04-23 13:59:18 +00:00
|
|
|
static struct ofw_compat_data compat_data[] = {
|
|
|
|
#ifdef SOC_ALLWINNER_A10
|
2020-01-16 20:02:41 +00:00
|
|
|
{"allwinner,sun4i-a10-pinctrl", (uintptr_t)&a10_gpio_conf},
|
2016-04-23 13:59:18 +00:00
|
|
|
#endif
|
2016-07-08 23:38:25 +00:00
|
|
|
#ifdef SOC_ALLWINNER_A13
|
2020-01-16 20:02:41 +00:00
|
|
|
{"allwinner,sun5i-a13-pinctrl", (uintptr_t)&a13_gpio_conf},
|
2016-07-08 23:38:25 +00:00
|
|
|
#endif
|
2016-04-23 13:59:18 +00:00
|
|
|
#ifdef SOC_ALLWINNER_A20
|
2020-01-16 20:02:41 +00:00
|
|
|
{"allwinner,sun7i-a20-pinctrl", (uintptr_t)&a20_gpio_conf},
|
2016-04-23 13:59:18 +00:00
|
|
|
#endif
|
|
|
|
#ifdef SOC_ALLWINNER_A31
|
2020-01-16 20:02:41 +00:00
|
|
|
{"allwinner,sun6i-a31-pinctrl", (uintptr_t)&a31_gpio_conf},
|
2016-04-23 13:59:18 +00:00
|
|
|
#endif
|
|
|
|
#ifdef SOC_ALLWINNER_A31S
|
2020-01-16 20:02:41 +00:00
|
|
|
{"allwinner,sun6i-a31s-pinctrl", (uintptr_t)&a31s_gpio_conf},
|
2016-04-23 13:59:18 +00:00
|
|
|
#endif
|
|
|
|
#if defined(SOC_ALLWINNER_A31) || defined(SOC_ALLWINNER_A31S)
|
2020-01-16 20:02:41 +00:00
|
|
|
{"allwinner,sun6i-a31-r-pinctrl", (uintptr_t)&a31_r_gpio_conf},
|
2016-05-05 09:41:57 +00:00
|
|
|
#endif
|
2017-01-04 03:35:39 +00:00
|
|
|
#ifdef SOC_ALLWINNER_A33
|
2020-01-16 20:02:41 +00:00
|
|
|
{"allwinner,sun6i-a33-pinctrl", (uintptr_t)&a33_gpio_conf},
|
2017-01-04 03:35:39 +00:00
|
|
|
#endif
|
2016-05-05 09:41:57 +00:00
|
|
|
#ifdef SOC_ALLWINNER_A83T
|
2020-01-16 20:02:41 +00:00
|
|
|
{"allwinner,sun8i-a83t-pinctrl", (uintptr_t)&a83t_gpio_conf},
|
|
|
|
{"allwinner,sun8i-a83t-r-pinctrl", (uintptr_t)&a83t_r_gpio_conf},
|
2016-05-13 18:20:54 +00:00
|
|
|
#endif
|
2017-12-05 21:21:23 +00:00
|
|
|
#if defined(SOC_ALLWINNER_H3) || defined(SOC_ALLWINNER_H5)
|
2020-01-16 20:02:41 +00:00
|
|
|
{"allwinner,sun8i-h3-pinctrl", (uintptr_t)&h3_gpio_conf},
|
|
|
|
{"allwinner,sun50i-h5-pinctrl", (uintptr_t)&h3_gpio_conf},
|
|
|
|
{"allwinner,sun8i-h3-r-pinctrl", (uintptr_t)&h3_r_gpio_conf},
|
2016-07-11 20:09:17 +00:00
|
|
|
#endif
|
|
|
|
#ifdef SOC_ALLWINNER_A64
|
2020-01-16 20:02:41 +00:00
|
|
|
{"allwinner,sun50i-a64-pinctrl", (uintptr_t)&a64_gpio_conf},
|
|
|
|
{"allwinner,sun50i-a64-r-pinctrl", (uintptr_t)&a64_r_gpio_conf},
|
2019-10-14 21:55:45 +00:00
|
|
|
#endif
|
|
|
|
#ifdef SOC_ALLWINNER_H6
|
2020-01-16 20:02:41 +00:00
|
|
|
{"allwinner,sun50i-h6-pinctrl", (uintptr_t)&h6_gpio_conf},
|
|
|
|
{"allwinner,sun50i-h6-r-pinctrl", (uintptr_t)&h6_r_gpio_conf},
|
2016-04-23 13:59:18 +00:00
|
|
|
#endif
|
|
|
|
{NULL, 0}
|
|
|
|
};
|
|
|
|
|
2017-09-26 20:23:09 +00:00
|
|
|
struct clk_list {
|
|
|
|
TAILQ_ENTRY(clk_list) next;
|
|
|
|
clk_t clk;
|
|
|
|
};
|
|
|
|
|
2020-01-19 19:51:20 +00:00
|
|
|
struct gpio_irqsrc {
|
|
|
|
struct intr_irqsrc isrc;
|
|
|
|
u_int irq;
|
|
|
|
uint32_t mode;
|
|
|
|
uint32_t pin;
|
|
|
|
uint32_t bank;
|
|
|
|
uint32_t intnum;
|
|
|
|
uint32_t intfunc;
|
|
|
|
uint32_t oldfunc;
|
|
|
|
bool enabled;
|
|
|
|
};
|
|
|
|
|
|
|
|
#define AW_GPIO_MEMRES 0
|
|
|
|
#define AW_GPIO_IRQRES 1
|
|
|
|
#define AW_GPIO_RESSZ 2
|
|
|
|
|
2017-12-26 12:11:04 +00:00
|
|
|
struct aw_gpio_softc {
|
2016-04-23 13:59:18 +00:00
|
|
|
device_t sc_dev;
|
|
|
|
device_t sc_busdev;
|
2020-01-19 19:51:20 +00:00
|
|
|
struct resource * sc_res[AW_GPIO_RESSZ];
|
2016-04-23 13:59:18 +00:00
|
|
|
struct mtx sc_mtx;
|
|
|
|
struct resource * sc_mem_res;
|
|
|
|
struct resource * sc_irq_res;
|
|
|
|
void * sc_intrhand;
|
2020-01-16 20:02:41 +00:00
|
|
|
struct aw_gpio_conf *conf;
|
2017-09-26 20:23:09 +00:00
|
|
|
TAILQ_HEAD(, clk_list) clk_list;
|
2020-01-19 19:51:20 +00:00
|
|
|
|
|
|
|
struct gpio_irqsrc *gpio_pic_irqsrc;
|
|
|
|
int nirqs;
|
|
|
|
};
|
|
|
|
|
|
|
|
static struct resource_spec aw_gpio_res_spec[] = {
|
|
|
|
{ SYS_RES_MEMORY, 0, RF_ACTIVE },
|
|
|
|
{ SYS_RES_IRQ, 0, RF_ACTIVE | RF_SHAREABLE },
|
|
|
|
{ -1, 0, 0 }
|
2016-04-23 13:59:18 +00:00
|
|
|
};
|
|
|
|
|
2017-12-26 12:11:04 +00:00
|
|
|
#define AW_GPIO_LOCK(_sc) mtx_lock_spin(&(_sc)->sc_mtx)
|
|
|
|
#define AW_GPIO_UNLOCK(_sc) mtx_unlock_spin(&(_sc)->sc_mtx)
|
|
|
|
#define AW_GPIO_LOCK_ASSERT(_sc) mtx_assert(&(_sc)->sc_mtx, MA_OWNED)
|
2013-02-05 02:25:13 +00:00
|
|
|
|
2017-12-26 12:11:04 +00:00
|
|
|
#define AW_GPIO_GP_CFG(_bank, _idx) 0x00 + ((_bank) * 0x24) + ((_idx) << 2)
|
|
|
|
#define AW_GPIO_GP_DAT(_bank) 0x10 + ((_bank) * 0x24)
|
|
|
|
#define AW_GPIO_GP_DRV(_bank, _idx) 0x14 + ((_bank) * 0x24) + ((_idx) << 2)
|
|
|
|
#define AW_GPIO_GP_PUL(_bank, _idx) 0x1c + ((_bank) * 0x24) + ((_idx) << 2)
|
2013-02-05 02:25:13 +00:00
|
|
|
|
2020-01-19 19:51:20 +00:00
|
|
|
#define AW_GPIO_GP_INT_BASE(_bank) (0x200 + 0x20 * _bank)
|
|
|
|
|
|
|
|
#define AW_GPIO_GP_INT_CFG(_bank, _pin) (AW_GPIO_GP_INT_BASE(_bank) + (0x4 * ((_pin) / 8)))
|
|
|
|
#define AW_GPIO_GP_INT_CTL(_bank) (AW_GPIO_GP_INT_BASE(_bank) + 0x10)
|
|
|
|
#define AW_GPIO_GP_INT_STA(_bank) (AW_GPIO_GP_INT_BASE(_bank) + 0x14)
|
|
|
|
#define AW_GPIO_GP_INT_DEB(_bank) (AW_GPIO_GP_INT_BASE(_bank) + 0x18)
|
2013-02-05 02:25:13 +00:00
|
|
|
|
2020-01-19 19:51:20 +00:00
|
|
|
#define AW_GPIO_INT_EDGE_POSITIVE 0x0
|
|
|
|
#define AW_GPIO_INT_EDGE_NEGATIVE 0x1
|
|
|
|
#define AW_GPIO_INT_LEVEL_HIGH 0x2
|
|
|
|
#define AW_GPIO_INT_LEVEL_LOW 0x3
|
|
|
|
#define AW_GPIO_INT_EDGE_BOTH 0x4
|
2013-02-05 02:25:13 +00:00
|
|
|
|
2017-12-26 12:11:04 +00:00
|
|
|
static char *aw_gpio_parse_function(phandle_t node);
|
|
|
|
static const char **aw_gpio_parse_pins(phandle_t node, int *pins_nb);
|
|
|
|
static uint32_t aw_gpio_parse_bias(phandle_t node);
|
|
|
|
static int aw_gpio_parse_drive_strength(phandle_t node, uint32_t *drive);
|
2017-12-01 20:51:08 +00:00
|
|
|
|
2017-12-26 12:11:04 +00:00
|
|
|
static int aw_gpio_pin_get(device_t dev, uint32_t pin, unsigned int *value);
|
|
|
|
static int aw_gpio_pin_set(device_t dev, uint32_t pin, unsigned int value);
|
|
|
|
static int aw_gpio_pin_get_locked(struct aw_gpio_softc *sc, uint32_t pin, unsigned int *value);
|
|
|
|
static int aw_gpio_pin_set_locked(struct aw_gpio_softc *sc, uint32_t pin, unsigned int value);
|
Add gpio methods to read/write/configure up to 32 pins simultaneously.
Sometimes it is necessary to combine several gpio pins into an ad-hoc bus
and manipulate the pins as a group. In such cases manipulating the pins
individualy is not an option, because the value on the "bus" assumes
potentially-invalid intermediate values as each pin is changed in turn. Note
that the "bus" may be something as simple as a bi-color LED where changing
colors requires changing both gpio pins at once, or something as complex as
a bitbanged multiplexed address/data bus connected to a microcontroller.
In addition to the absolute requirement of simultaneously changing the
output values of driven pins, a desirable feature of these new methods is to
provide a higher-performance mechanism for reading and writing multiple
pins, especially from userland where pin-at-a-time access incurs a noticible
syscall time penalty.
These new interfaces are NOT intended to abstract away all the ugly details
of how gpio is implemented on any given platform. In fact, to use these
properly you absolutely must know something about how the gpio hardware is
organized. Typically there are "banks" of gpio pins controlled by registers
which group several pins together. A bank may be as small as 2 pins or as
big as "all the pins on the device, hundreds of them." In the latter case, a
driver might support this interface by allowing access to any 32 adjacent
pins within the overall collection. Or, more likely, any 32 adjacent pins
starting at any multiple of 32. Whatever the hardware restrictions may be,
you would need to understand them to use this interface.
In additional to defining the interfaces, two example implementations are
included here, for imx5/6, and allwinner. These represent the two primary
types of gpio hardware drivers. imx6 has multiple gpio devices, each
implementing a single bank of 32 pins. Allwinner implements a single large
gpio number space from 1-n pins, and the driver internally translates that
linear number space to a bank+pin scheme based on how the pins are grouped
into control registers. The allwinner implementation imposes the restriction
that the first_pin argument to the new functions must always be pin 0 of a
bank.
Differential Revision: https://reviews.freebsd.org/D11810
2017-09-10 18:08:25 +00:00
|
|
|
|
2020-01-19 19:51:20 +00:00
|
|
|
static void aw_gpio_intr(void *arg);
|
|
|
|
static void aw_gpio_pic_disable_intr(device_t dev, struct intr_irqsrc *isrc);
|
|
|
|
static void aw_gpio_pic_disable_intr_locked(struct aw_gpio_softc *sc, struct intr_irqsrc *isrc);
|
|
|
|
static void aw_gpio_pic_post_filter(device_t dev, struct intr_irqsrc *isrc);
|
|
|
|
static int aw_gpio_register_isrcs(struct aw_gpio_softc *sc);
|
|
|
|
|
2017-12-26 12:11:04 +00:00
|
|
|
#define AW_GPIO_WRITE(_sc, _off, _val) \
|
2020-01-19 19:51:20 +00:00
|
|
|
bus_write_4((_sc)->sc_res[AW_GPIO_MEMRES], _off, _val)
|
2017-12-26 12:11:04 +00:00
|
|
|
#define AW_GPIO_READ(_sc, _off) \
|
2020-01-19 19:51:20 +00:00
|
|
|
bus_read_4((_sc)->sc_res[AW_GPIO_MEMRES], _off)
|
2013-02-05 02:25:13 +00:00
|
|
|
|
|
|
|
static uint32_t
|
2017-12-26 12:11:04 +00:00
|
|
|
aw_gpio_get_function(struct aw_gpio_softc *sc, uint32_t pin)
|
2013-02-05 02:25:13 +00:00
|
|
|
{
|
|
|
|
uint32_t bank, func, offset;
|
|
|
|
|
2015-07-13 18:19:26 +00:00
|
|
|
/* Must be called with lock held. */
|
2017-12-26 12:11:04 +00:00
|
|
|
AW_GPIO_LOCK_ASSERT(sc);
|
2015-07-13 18:19:26 +00:00
|
|
|
|
2020-01-16 20:02:41 +00:00
|
|
|
if (pin > sc->conf->padconf->npins)
|
2016-02-17 18:28:03 +00:00
|
|
|
return (0);
|
2020-01-16 20:02:41 +00:00
|
|
|
bank = sc->conf->padconf->pins[pin].port;
|
|
|
|
pin = sc->conf->padconf->pins[pin].pin;
|
2013-02-05 02:25:13 +00:00
|
|
|
offset = ((pin & 0x07) << 2);
|
|
|
|
|
2017-12-26 12:11:04 +00:00
|
|
|
func = AW_GPIO_READ(sc, AW_GPIO_GP_CFG(bank, pin >> 3));
|
2015-07-13 18:19:26 +00:00
|
|
|
|
2016-08-17 10:20:36 +00:00
|
|
|
return ((func >> offset) & 0x7);
|
2013-02-05 02:25:13 +00:00
|
|
|
}
|
|
|
|
|
2016-07-08 23:08:59 +00:00
|
|
|
static int
|
2017-12-26 12:11:04 +00:00
|
|
|
aw_gpio_set_function(struct aw_gpio_softc *sc, uint32_t pin, uint32_t f)
|
2013-02-05 02:25:13 +00:00
|
|
|
{
|
2015-07-13 18:19:26 +00:00
|
|
|
uint32_t bank, data, offset;
|
2013-02-05 02:25:13 +00:00
|
|
|
|
2016-07-08 23:08:59 +00:00
|
|
|
/* Check if the function exists in the padconf data */
|
2020-01-16 20:02:41 +00:00
|
|
|
if (sc->conf->padconf->pins[pin].functions[f] == NULL)
|
2016-07-08 23:08:59 +00:00
|
|
|
return (EINVAL);
|
|
|
|
|
2013-02-05 02:25:13 +00:00
|
|
|
/* Must be called with lock held. */
|
2017-12-26 12:11:04 +00:00
|
|
|
AW_GPIO_LOCK_ASSERT(sc);
|
2013-02-05 02:25:13 +00:00
|
|
|
|
2020-01-16 20:02:41 +00:00
|
|
|
bank = sc->conf->padconf->pins[pin].port;
|
|
|
|
pin = sc->conf->padconf->pins[pin].pin;
|
2013-02-05 02:25:13 +00:00
|
|
|
offset = ((pin & 0x07) << 2);
|
|
|
|
|
2017-12-26 12:11:04 +00:00
|
|
|
data = AW_GPIO_READ(sc, AW_GPIO_GP_CFG(bank, pin >> 3));
|
2013-02-05 02:25:13 +00:00
|
|
|
data &= ~(7 << offset);
|
|
|
|
data |= (f << offset);
|
2017-12-26 12:11:04 +00:00
|
|
|
AW_GPIO_WRITE(sc, AW_GPIO_GP_CFG(bank, pin >> 3), data);
|
2016-07-08 23:08:59 +00:00
|
|
|
|
|
|
|
return (0);
|
2015-07-13 18:19:26 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static uint32_t
|
2017-12-26 12:11:04 +00:00
|
|
|
aw_gpio_get_pud(struct aw_gpio_softc *sc, uint32_t pin)
|
2015-07-13 18:19:26 +00:00
|
|
|
{
|
|
|
|
uint32_t bank, offset, val;
|
|
|
|
|
|
|
|
/* Must be called with lock held. */
|
2017-12-26 12:11:04 +00:00
|
|
|
AW_GPIO_LOCK_ASSERT(sc);
|
2015-07-13 18:19:26 +00:00
|
|
|
|
2020-01-16 20:02:41 +00:00
|
|
|
bank = sc->conf->padconf->pins[pin].port;
|
|
|
|
pin = sc->conf->padconf->pins[pin].pin;
|
2015-07-13 18:19:26 +00:00
|
|
|
offset = ((pin & 0x0f) << 1);
|
|
|
|
|
2017-12-26 12:11:04 +00:00
|
|
|
val = AW_GPIO_READ(sc, AW_GPIO_GP_PUL(bank, pin >> 4));
|
2015-07-13 18:19:26 +00:00
|
|
|
|
2016-08-17 10:20:36 +00:00
|
|
|
return ((val >> offset) & AW_GPIO_PUD_MASK);
|
2013-02-05 02:25:13 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2017-12-26 12:11:04 +00:00
|
|
|
aw_gpio_set_pud(struct aw_gpio_softc *sc, uint32_t pin, uint32_t state)
|
2013-02-05 02:25:13 +00:00
|
|
|
{
|
2015-07-13 18:19:26 +00:00
|
|
|
uint32_t bank, offset, val;
|
2013-02-05 02:25:13 +00:00
|
|
|
|
2017-12-26 12:11:04 +00:00
|
|
|
if (aw_gpio_get_pud(sc, pin) == state)
|
2017-12-01 20:51:08 +00:00
|
|
|
return;
|
|
|
|
|
2013-02-05 02:25:13 +00:00
|
|
|
/* Must be called with lock held. */
|
2017-12-26 12:11:04 +00:00
|
|
|
AW_GPIO_LOCK_ASSERT(sc);
|
2013-02-05 02:25:13 +00:00
|
|
|
|
2020-01-16 20:02:41 +00:00
|
|
|
bank = sc->conf->padconf->pins[pin].port;
|
|
|
|
pin = sc->conf->padconf->pins[pin].pin;
|
2013-02-05 02:25:13 +00:00
|
|
|
offset = ((pin & 0x0f) << 1);
|
|
|
|
|
2017-12-26 12:11:04 +00:00
|
|
|
val = AW_GPIO_READ(sc, AW_GPIO_GP_PUL(bank, pin >> 4));
|
2016-02-17 18:28:03 +00:00
|
|
|
val &= ~(AW_GPIO_PUD_MASK << offset);
|
2013-02-05 02:25:13 +00:00
|
|
|
val |= (state << offset);
|
2017-12-26 12:11:04 +00:00
|
|
|
AW_GPIO_WRITE(sc, AW_GPIO_GP_PUL(bank, pin >> 4), val);
|
2013-02-05 02:25:13 +00:00
|
|
|
}
|
|
|
|
|
2016-08-17 10:20:36 +00:00
|
|
|
static uint32_t
|
2017-12-26 12:11:04 +00:00
|
|
|
aw_gpio_get_drv(struct aw_gpio_softc *sc, uint32_t pin)
|
2016-08-17 10:20:36 +00:00
|
|
|
{
|
|
|
|
uint32_t bank, offset, val;
|
|
|
|
|
|
|
|
/* Must be called with lock held. */
|
2017-12-26 12:11:04 +00:00
|
|
|
AW_GPIO_LOCK_ASSERT(sc);
|
2016-08-17 10:20:36 +00:00
|
|
|
|
2020-01-16 20:02:41 +00:00
|
|
|
bank = sc->conf->padconf->pins[pin].port;
|
|
|
|
pin = sc->conf->padconf->pins[pin].pin;
|
2016-08-17 10:20:36 +00:00
|
|
|
offset = ((pin & 0x0f) << 1);
|
|
|
|
|
2017-12-26 12:11:04 +00:00
|
|
|
val = AW_GPIO_READ(sc, AW_GPIO_GP_DRV(bank, pin >> 4));
|
2016-08-17 10:20:36 +00:00
|
|
|
|
|
|
|
return ((val >> offset) & AW_GPIO_DRV_MASK);
|
|
|
|
}
|
|
|
|
|
2016-02-17 18:28:03 +00:00
|
|
|
static void
|
2017-12-26 12:11:04 +00:00
|
|
|
aw_gpio_set_drv(struct aw_gpio_softc *sc, uint32_t pin, uint32_t drive)
|
2016-02-17 18:28:03 +00:00
|
|
|
{
|
|
|
|
uint32_t bank, offset, val;
|
|
|
|
|
2017-12-26 12:11:04 +00:00
|
|
|
if (aw_gpio_get_drv(sc, pin) == drive)
|
2017-12-01 20:51:08 +00:00
|
|
|
return;
|
|
|
|
|
2016-02-17 18:28:03 +00:00
|
|
|
/* Must be called with lock held. */
|
2017-12-26 12:11:04 +00:00
|
|
|
AW_GPIO_LOCK_ASSERT(sc);
|
2016-02-17 18:28:03 +00:00
|
|
|
|
2020-01-16 20:02:41 +00:00
|
|
|
bank = sc->conf->padconf->pins[pin].port;
|
|
|
|
pin = sc->conf->padconf->pins[pin].pin;
|
2016-02-17 18:28:03 +00:00
|
|
|
offset = ((pin & 0x0f) << 1);
|
|
|
|
|
2017-12-26 12:11:04 +00:00
|
|
|
val = AW_GPIO_READ(sc, AW_GPIO_GP_DRV(bank, pin >> 4));
|
2016-02-17 18:28:03 +00:00
|
|
|
val &= ~(AW_GPIO_DRV_MASK << offset);
|
|
|
|
val |= (drive << offset);
|
2017-12-26 12:11:04 +00:00
|
|
|
AW_GPIO_WRITE(sc, AW_GPIO_GP_DRV(bank, pin >> 4), val);
|
2016-02-17 18:28:03 +00:00
|
|
|
}
|
|
|
|
|
2016-07-08 23:08:59 +00:00
|
|
|
static int
|
2017-12-26 12:11:04 +00:00
|
|
|
aw_gpio_pin_configure(struct aw_gpio_softc *sc, uint32_t pin, uint32_t flags)
|
2013-02-05 02:25:13 +00:00
|
|
|
{
|
Add gpio methods to read/write/configure up to 32 pins simultaneously.
Sometimes it is necessary to combine several gpio pins into an ad-hoc bus
and manipulate the pins as a group. In such cases manipulating the pins
individualy is not an option, because the value on the "bus" assumes
potentially-invalid intermediate values as each pin is changed in turn. Note
that the "bus" may be something as simple as a bi-color LED where changing
colors requires changing both gpio pins at once, or something as complex as
a bitbanged multiplexed address/data bus connected to a microcontroller.
In addition to the absolute requirement of simultaneously changing the
output values of driven pins, a desirable feature of these new methods is to
provide a higher-performance mechanism for reading and writing multiple
pins, especially from userland where pin-at-a-time access incurs a noticible
syscall time penalty.
These new interfaces are NOT intended to abstract away all the ugly details
of how gpio is implemented on any given platform. In fact, to use these
properly you absolutely must know something about how the gpio hardware is
organized. Typically there are "banks" of gpio pins controlled by registers
which group several pins together. A bank may be as small as 2 pins or as
big as "all the pins on the device, hundreds of them." In the latter case, a
driver might support this interface by allowing access to any 32 adjacent
pins within the overall collection. Or, more likely, any 32 adjacent pins
starting at any multiple of 32. Whatever the hardware restrictions may be,
you would need to understand them to use this interface.
In additional to defining the interfaces, two example implementations are
included here, for imx5/6, and allwinner. These represent the two primary
types of gpio hardware drivers. imx6 has multiple gpio devices, each
implementing a single bank of 32 pins. Allwinner implements a single large
gpio number space from 1-n pins, and the driver internally translates that
linear number space to a bank+pin scheme based on how the pins are grouped
into control registers. The allwinner implementation imposes the restriction
that the first_pin argument to the new functions must always be pin 0 of a
bank.
Differential Revision: https://reviews.freebsd.org/D11810
2017-09-10 18:08:25 +00:00
|
|
|
u_int val;
|
2016-07-08 23:08:59 +00:00
|
|
|
int err = 0;
|
2013-02-05 02:25:13 +00:00
|
|
|
|
2015-07-13 18:19:26 +00:00
|
|
|
/* Must be called with lock held. */
|
2017-12-26 12:11:04 +00:00
|
|
|
AW_GPIO_LOCK_ASSERT(sc);
|
2013-02-05 02:25:13 +00:00
|
|
|
|
2020-01-16 20:02:41 +00:00
|
|
|
if (pin > sc->conf->padconf->npins)
|
Add gpio methods to read/write/configure up to 32 pins simultaneously.
Sometimes it is necessary to combine several gpio pins into an ad-hoc bus
and manipulate the pins as a group. In such cases manipulating the pins
individualy is not an option, because the value on the "bus" assumes
potentially-invalid intermediate values as each pin is changed in turn. Note
that the "bus" may be something as simple as a bi-color LED where changing
colors requires changing both gpio pins at once, or something as complex as
a bitbanged multiplexed address/data bus connected to a microcontroller.
In addition to the absolute requirement of simultaneously changing the
output values of driven pins, a desirable feature of these new methods is to
provide a higher-performance mechanism for reading and writing multiple
pins, especially from userland where pin-at-a-time access incurs a noticible
syscall time penalty.
These new interfaces are NOT intended to abstract away all the ugly details
of how gpio is implemented on any given platform. In fact, to use these
properly you absolutely must know something about how the gpio hardware is
organized. Typically there are "banks" of gpio pins controlled by registers
which group several pins together. A bank may be as small as 2 pins or as
big as "all the pins on the device, hundreds of them." In the latter case, a
driver might support this interface by allowing access to any 32 adjacent
pins within the overall collection. Or, more likely, any 32 adjacent pins
starting at any multiple of 32. Whatever the hardware restrictions may be,
you would need to understand them to use this interface.
In additional to defining the interfaces, two example implementations are
included here, for imx5/6, and allwinner. These represent the two primary
types of gpio hardware drivers. imx6 has multiple gpio devices, each
implementing a single bank of 32 pins. Allwinner implements a single large
gpio number space from 1-n pins, and the driver internally translates that
linear number space to a bank+pin scheme based on how the pins are grouped
into control registers. The allwinner implementation imposes the restriction
that the first_pin argument to the new functions must always be pin 0 of a
bank.
Differential Revision: https://reviews.freebsd.org/D11810
2017-09-10 18:08:25 +00:00
|
|
|
return (EINVAL);
|
|
|
|
|
2015-07-13 18:19:26 +00:00
|
|
|
/* Manage input/output. */
|
Add gpio methods to read/write/configure up to 32 pins simultaneously.
Sometimes it is necessary to combine several gpio pins into an ad-hoc bus
and manipulate the pins as a group. In such cases manipulating the pins
individualy is not an option, because the value on the "bus" assumes
potentially-invalid intermediate values as each pin is changed in turn. Note
that the "bus" may be something as simple as a bi-color LED where changing
colors requires changing both gpio pins at once, or something as complex as
a bitbanged multiplexed address/data bus connected to a microcontroller.
In addition to the absolute requirement of simultaneously changing the
output values of driven pins, a desirable feature of these new methods is to
provide a higher-performance mechanism for reading and writing multiple
pins, especially from userland where pin-at-a-time access incurs a noticible
syscall time penalty.
These new interfaces are NOT intended to abstract away all the ugly details
of how gpio is implemented on any given platform. In fact, to use these
properly you absolutely must know something about how the gpio hardware is
organized. Typically there are "banks" of gpio pins controlled by registers
which group several pins together. A bank may be as small as 2 pins or as
big as "all the pins on the device, hundreds of them." In the latter case, a
driver might support this interface by allowing access to any 32 adjacent
pins within the overall collection. Or, more likely, any 32 adjacent pins
starting at any multiple of 32. Whatever the hardware restrictions may be,
you would need to understand them to use this interface.
In additional to defining the interfaces, two example implementations are
included here, for imx5/6, and allwinner. These represent the two primary
types of gpio hardware drivers. imx6 has multiple gpio devices, each
implementing a single bank of 32 pins. Allwinner implements a single large
gpio number space from 1-n pins, and the driver internally translates that
linear number space to a bank+pin scheme based on how the pins are grouped
into control registers. The allwinner implementation imposes the restriction
that the first_pin argument to the new functions must always be pin 0 of a
bank.
Differential Revision: https://reviews.freebsd.org/D11810
2017-09-10 18:08:25 +00:00
|
|
|
if (flags & GPIO_PIN_INPUT) {
|
2017-12-26 12:11:04 +00:00
|
|
|
err = aw_gpio_set_function(sc, pin, AW_GPIO_INPUT);
|
2017-12-05 21:40:52 +00:00
|
|
|
} else if ((flags & GPIO_PIN_OUTPUT) &&
|
2017-12-26 12:11:04 +00:00
|
|
|
aw_gpio_get_function(sc, pin) != AW_GPIO_OUTPUT) {
|
Add gpio methods to read/write/configure up to 32 pins simultaneously.
Sometimes it is necessary to combine several gpio pins into an ad-hoc bus
and manipulate the pins as a group. In such cases manipulating the pins
individualy is not an option, because the value on the "bus" assumes
potentially-invalid intermediate values as each pin is changed in turn. Note
that the "bus" may be something as simple as a bi-color LED where changing
colors requires changing both gpio pins at once, or something as complex as
a bitbanged multiplexed address/data bus connected to a microcontroller.
In addition to the absolute requirement of simultaneously changing the
output values of driven pins, a desirable feature of these new methods is to
provide a higher-performance mechanism for reading and writing multiple
pins, especially from userland where pin-at-a-time access incurs a noticible
syscall time penalty.
These new interfaces are NOT intended to abstract away all the ugly details
of how gpio is implemented on any given platform. In fact, to use these
properly you absolutely must know something about how the gpio hardware is
organized. Typically there are "banks" of gpio pins controlled by registers
which group several pins together. A bank may be as small as 2 pins or as
big as "all the pins on the device, hundreds of them." In the latter case, a
driver might support this interface by allowing access to any 32 adjacent
pins within the overall collection. Or, more likely, any 32 adjacent pins
starting at any multiple of 32. Whatever the hardware restrictions may be,
you would need to understand them to use this interface.
In additional to defining the interfaces, two example implementations are
included here, for imx5/6, and allwinner. These represent the two primary
types of gpio hardware drivers. imx6 has multiple gpio devices, each
implementing a single bank of 32 pins. Allwinner implements a single large
gpio number space from 1-n pins, and the driver internally translates that
linear number space to a bank+pin scheme based on how the pins are grouped
into control registers. The allwinner implementation imposes the restriction
that the first_pin argument to the new functions must always be pin 0 of a
bank.
Differential Revision: https://reviews.freebsd.org/D11810
2017-09-10 18:08:25 +00:00
|
|
|
if (flags & GPIO_PIN_PRESET_LOW) {
|
2017-12-26 12:11:04 +00:00
|
|
|
aw_gpio_pin_set_locked(sc, pin, 0);
|
Add gpio methods to read/write/configure up to 32 pins simultaneously.
Sometimes it is necessary to combine several gpio pins into an ad-hoc bus
and manipulate the pins as a group. In such cases manipulating the pins
individualy is not an option, because the value on the "bus" assumes
potentially-invalid intermediate values as each pin is changed in turn. Note
that the "bus" may be something as simple as a bi-color LED where changing
colors requires changing both gpio pins at once, or something as complex as
a bitbanged multiplexed address/data bus connected to a microcontroller.
In addition to the absolute requirement of simultaneously changing the
output values of driven pins, a desirable feature of these new methods is to
provide a higher-performance mechanism for reading and writing multiple
pins, especially from userland where pin-at-a-time access incurs a noticible
syscall time penalty.
These new interfaces are NOT intended to abstract away all the ugly details
of how gpio is implemented on any given platform. In fact, to use these
properly you absolutely must know something about how the gpio hardware is
organized. Typically there are "banks" of gpio pins controlled by registers
which group several pins together. A bank may be as small as 2 pins or as
big as "all the pins on the device, hundreds of them." In the latter case, a
driver might support this interface by allowing access to any 32 adjacent
pins within the overall collection. Or, more likely, any 32 adjacent pins
starting at any multiple of 32. Whatever the hardware restrictions may be,
you would need to understand them to use this interface.
In additional to defining the interfaces, two example implementations are
included here, for imx5/6, and allwinner. These represent the two primary
types of gpio hardware drivers. imx6 has multiple gpio devices, each
implementing a single bank of 32 pins. Allwinner implements a single large
gpio number space from 1-n pins, and the driver internally translates that
linear number space to a bank+pin scheme based on how the pins are grouped
into control registers. The allwinner implementation imposes the restriction
that the first_pin argument to the new functions must always be pin 0 of a
bank.
Differential Revision: https://reviews.freebsd.org/D11810
2017-09-10 18:08:25 +00:00
|
|
|
} else if (flags & GPIO_PIN_PRESET_HIGH) {
|
2017-12-26 12:11:04 +00:00
|
|
|
aw_gpio_pin_set_locked(sc, pin, 1);
|
Add gpio methods to read/write/configure up to 32 pins simultaneously.
Sometimes it is necessary to combine several gpio pins into an ad-hoc bus
and manipulate the pins as a group. In such cases manipulating the pins
individualy is not an option, because the value on the "bus" assumes
potentially-invalid intermediate values as each pin is changed in turn. Note
that the "bus" may be something as simple as a bi-color LED where changing
colors requires changing both gpio pins at once, or something as complex as
a bitbanged multiplexed address/data bus connected to a microcontroller.
In addition to the absolute requirement of simultaneously changing the
output values of driven pins, a desirable feature of these new methods is to
provide a higher-performance mechanism for reading and writing multiple
pins, especially from userland where pin-at-a-time access incurs a noticible
syscall time penalty.
These new interfaces are NOT intended to abstract away all the ugly details
of how gpio is implemented on any given platform. In fact, to use these
properly you absolutely must know something about how the gpio hardware is
organized. Typically there are "banks" of gpio pins controlled by registers
which group several pins together. A bank may be as small as 2 pins or as
big as "all the pins on the device, hundreds of them." In the latter case, a
driver might support this interface by allowing access to any 32 adjacent
pins within the overall collection. Or, more likely, any 32 adjacent pins
starting at any multiple of 32. Whatever the hardware restrictions may be,
you would need to understand them to use this interface.
In additional to defining the interfaces, two example implementations are
included here, for imx5/6, and allwinner. These represent the two primary
types of gpio hardware drivers. imx6 has multiple gpio devices, each
implementing a single bank of 32 pins. Allwinner implements a single large
gpio number space from 1-n pins, and the driver internally translates that
linear number space to a bank+pin scheme based on how the pins are grouped
into control registers. The allwinner implementation imposes the restriction
that the first_pin argument to the new functions must always be pin 0 of a
bank.
Differential Revision: https://reviews.freebsd.org/D11810
2017-09-10 18:08:25 +00:00
|
|
|
} else {
|
|
|
|
/* Read the pin and preset output to current state. */
|
2017-12-26 12:11:04 +00:00
|
|
|
err = aw_gpio_set_function(sc, pin, AW_GPIO_INPUT);
|
Add gpio methods to read/write/configure up to 32 pins simultaneously.
Sometimes it is necessary to combine several gpio pins into an ad-hoc bus
and manipulate the pins as a group. In such cases manipulating the pins
individualy is not an option, because the value on the "bus" assumes
potentially-invalid intermediate values as each pin is changed in turn. Note
that the "bus" may be something as simple as a bi-color LED where changing
colors requires changing both gpio pins at once, or something as complex as
a bitbanged multiplexed address/data bus connected to a microcontroller.
In addition to the absolute requirement of simultaneously changing the
output values of driven pins, a desirable feature of these new methods is to
provide a higher-performance mechanism for reading and writing multiple
pins, especially from userland where pin-at-a-time access incurs a noticible
syscall time penalty.
These new interfaces are NOT intended to abstract away all the ugly details
of how gpio is implemented on any given platform. In fact, to use these
properly you absolutely must know something about how the gpio hardware is
organized. Typically there are "banks" of gpio pins controlled by registers
which group several pins together. A bank may be as small as 2 pins or as
big as "all the pins on the device, hundreds of them." In the latter case, a
driver might support this interface by allowing access to any 32 adjacent
pins within the overall collection. Or, more likely, any 32 adjacent pins
starting at any multiple of 32. Whatever the hardware restrictions may be,
you would need to understand them to use this interface.
In additional to defining the interfaces, two example implementations are
included here, for imx5/6, and allwinner. These represent the two primary
types of gpio hardware drivers. imx6 has multiple gpio devices, each
implementing a single bank of 32 pins. Allwinner implements a single large
gpio number space from 1-n pins, and the driver internally translates that
linear number space to a bank+pin scheme based on how the pins are grouped
into control registers. The allwinner implementation imposes the restriction
that the first_pin argument to the new functions must always be pin 0 of a
bank.
Differential Revision: https://reviews.freebsd.org/D11810
2017-09-10 18:08:25 +00:00
|
|
|
if (err == 0) {
|
2017-12-26 12:11:04 +00:00
|
|
|
aw_gpio_pin_get_locked(sc, pin, &val);
|
|
|
|
aw_gpio_pin_set_locked(sc, pin, val);
|
Add gpio methods to read/write/configure up to 32 pins simultaneously.
Sometimes it is necessary to combine several gpio pins into an ad-hoc bus
and manipulate the pins as a group. In such cases manipulating the pins
individualy is not an option, because the value on the "bus" assumes
potentially-invalid intermediate values as each pin is changed in turn. Note
that the "bus" may be something as simple as a bi-color LED where changing
colors requires changing both gpio pins at once, or something as complex as
a bitbanged multiplexed address/data bus connected to a microcontroller.
In addition to the absolute requirement of simultaneously changing the
output values of driven pins, a desirable feature of these new methods is to
provide a higher-performance mechanism for reading and writing multiple
pins, especially from userland where pin-at-a-time access incurs a noticible
syscall time penalty.
These new interfaces are NOT intended to abstract away all the ugly details
of how gpio is implemented on any given platform. In fact, to use these
properly you absolutely must know something about how the gpio hardware is
organized. Typically there are "banks" of gpio pins controlled by registers
which group several pins together. A bank may be as small as 2 pins or as
big as "all the pins on the device, hundreds of them." In the latter case, a
driver might support this interface by allowing access to any 32 adjacent
pins within the overall collection. Or, more likely, any 32 adjacent pins
starting at any multiple of 32. Whatever the hardware restrictions may be,
you would need to understand them to use this interface.
In additional to defining the interfaces, two example implementations are
included here, for imx5/6, and allwinner. These represent the two primary
types of gpio hardware drivers. imx6 has multiple gpio devices, each
implementing a single bank of 32 pins. Allwinner implements a single large
gpio number space from 1-n pins, and the driver internally translates that
linear number space to a bank+pin scheme based on how the pins are grouped
into control registers. The allwinner implementation imposes the restriction
that the first_pin argument to the new functions must always be pin 0 of a
bank.
Differential Revision: https://reviews.freebsd.org/D11810
2017-09-10 18:08:25 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
if (err == 0)
|
2017-12-26 12:11:04 +00:00
|
|
|
err = aw_gpio_set_function(sc, pin, AW_GPIO_OUTPUT);
|
2013-02-05 02:25:13 +00:00
|
|
|
}
|
|
|
|
|
2016-07-08 23:08:59 +00:00
|
|
|
if (err)
|
|
|
|
return (err);
|
|
|
|
|
2013-02-05 02:25:13 +00:00
|
|
|
/* Manage Pull-up/pull-down. */
|
Add gpio methods to read/write/configure up to 32 pins simultaneously.
Sometimes it is necessary to combine several gpio pins into an ad-hoc bus
and manipulate the pins as a group. In such cases manipulating the pins
individualy is not an option, because the value on the "bus" assumes
potentially-invalid intermediate values as each pin is changed in turn. Note
that the "bus" may be something as simple as a bi-color LED where changing
colors requires changing both gpio pins at once, or something as complex as
a bitbanged multiplexed address/data bus connected to a microcontroller.
In addition to the absolute requirement of simultaneously changing the
output values of driven pins, a desirable feature of these new methods is to
provide a higher-performance mechanism for reading and writing multiple
pins, especially from userland where pin-at-a-time access incurs a noticible
syscall time penalty.
These new interfaces are NOT intended to abstract away all the ugly details
of how gpio is implemented on any given platform. In fact, to use these
properly you absolutely must know something about how the gpio hardware is
organized. Typically there are "banks" of gpio pins controlled by registers
which group several pins together. A bank may be as small as 2 pins or as
big as "all the pins on the device, hundreds of them." In the latter case, a
driver might support this interface by allowing access to any 32 adjacent
pins within the overall collection. Or, more likely, any 32 adjacent pins
starting at any multiple of 32. Whatever the hardware restrictions may be,
you would need to understand them to use this interface.
In additional to defining the interfaces, two example implementations are
included here, for imx5/6, and allwinner. These represent the two primary
types of gpio hardware drivers. imx6 has multiple gpio devices, each
implementing a single bank of 32 pins. Allwinner implements a single large
gpio number space from 1-n pins, and the driver internally translates that
linear number space to a bank+pin scheme based on how the pins are grouped
into control registers. The allwinner implementation imposes the restriction
that the first_pin argument to the new functions must always be pin 0 of a
bank.
Differential Revision: https://reviews.freebsd.org/D11810
2017-09-10 18:08:25 +00:00
|
|
|
if (flags & GPIO_PIN_PULLUP)
|
2017-12-26 12:11:04 +00:00
|
|
|
aw_gpio_set_pud(sc, pin, AW_GPIO_PULLUP);
|
Add gpio methods to read/write/configure up to 32 pins simultaneously.
Sometimes it is necessary to combine several gpio pins into an ad-hoc bus
and manipulate the pins as a group. In such cases manipulating the pins
individualy is not an option, because the value on the "bus" assumes
potentially-invalid intermediate values as each pin is changed in turn. Note
that the "bus" may be something as simple as a bi-color LED where changing
colors requires changing both gpio pins at once, or something as complex as
a bitbanged multiplexed address/data bus connected to a microcontroller.
In addition to the absolute requirement of simultaneously changing the
output values of driven pins, a desirable feature of these new methods is to
provide a higher-performance mechanism for reading and writing multiple
pins, especially from userland where pin-at-a-time access incurs a noticible
syscall time penalty.
These new interfaces are NOT intended to abstract away all the ugly details
of how gpio is implemented on any given platform. In fact, to use these
properly you absolutely must know something about how the gpio hardware is
organized. Typically there are "banks" of gpio pins controlled by registers
which group several pins together. A bank may be as small as 2 pins or as
big as "all the pins on the device, hundreds of them." In the latter case, a
driver might support this interface by allowing access to any 32 adjacent
pins within the overall collection. Or, more likely, any 32 adjacent pins
starting at any multiple of 32. Whatever the hardware restrictions may be,
you would need to understand them to use this interface.
In additional to defining the interfaces, two example implementations are
included here, for imx5/6, and allwinner. These represent the two primary
types of gpio hardware drivers. imx6 has multiple gpio devices, each
implementing a single bank of 32 pins. Allwinner implements a single large
gpio number space from 1-n pins, and the driver internally translates that
linear number space to a bank+pin scheme based on how the pins are grouped
into control registers. The allwinner implementation imposes the restriction
that the first_pin argument to the new functions must always be pin 0 of a
bank.
Differential Revision: https://reviews.freebsd.org/D11810
2017-09-10 18:08:25 +00:00
|
|
|
else if (flags & GPIO_PIN_PULLDOWN)
|
2017-12-26 12:11:04 +00:00
|
|
|
aw_gpio_set_pud(sc, pin, AW_GPIO_PULLDOWN);
|
Add gpio methods to read/write/configure up to 32 pins simultaneously.
Sometimes it is necessary to combine several gpio pins into an ad-hoc bus
and manipulate the pins as a group. In such cases manipulating the pins
individualy is not an option, because the value on the "bus" assumes
potentially-invalid intermediate values as each pin is changed in turn. Note
that the "bus" may be something as simple as a bi-color LED where changing
colors requires changing both gpio pins at once, or something as complex as
a bitbanged multiplexed address/data bus connected to a microcontroller.
In addition to the absolute requirement of simultaneously changing the
output values of driven pins, a desirable feature of these new methods is to
provide a higher-performance mechanism for reading and writing multiple
pins, especially from userland where pin-at-a-time access incurs a noticible
syscall time penalty.
These new interfaces are NOT intended to abstract away all the ugly details
of how gpio is implemented on any given platform. In fact, to use these
properly you absolutely must know something about how the gpio hardware is
organized. Typically there are "banks" of gpio pins controlled by registers
which group several pins together. A bank may be as small as 2 pins or as
big as "all the pins on the device, hundreds of them." In the latter case, a
driver might support this interface by allowing access to any 32 adjacent
pins within the overall collection. Or, more likely, any 32 adjacent pins
starting at any multiple of 32. Whatever the hardware restrictions may be,
you would need to understand them to use this interface.
In additional to defining the interfaces, two example implementations are
included here, for imx5/6, and allwinner. These represent the two primary
types of gpio hardware drivers. imx6 has multiple gpio devices, each
implementing a single bank of 32 pins. Allwinner implements a single large
gpio number space from 1-n pins, and the driver internally translates that
linear number space to a bank+pin scheme based on how the pins are grouped
into control registers. The allwinner implementation imposes the restriction
that the first_pin argument to the new functions must always be pin 0 of a
bank.
Differential Revision: https://reviews.freebsd.org/D11810
2017-09-10 18:08:25 +00:00
|
|
|
else
|
2017-12-26 12:11:04 +00:00
|
|
|
aw_gpio_set_pud(sc, pin, AW_GPIO_NONE);
|
2016-07-08 23:08:59 +00:00
|
|
|
|
|
|
|
return (0);
|
2013-02-05 02:25:13 +00:00
|
|
|
}
|
|
|
|
|
2015-01-31 19:32:14 +00:00
|
|
|
static device_t
|
2017-12-26 12:11:04 +00:00
|
|
|
aw_gpio_get_bus(device_t dev)
|
2015-01-31 19:32:14 +00:00
|
|
|
{
|
2017-12-26 12:11:04 +00:00
|
|
|
struct aw_gpio_softc *sc;
|
2015-01-31 19:32:14 +00:00
|
|
|
|
|
|
|
sc = device_get_softc(dev);
|
|
|
|
|
|
|
|
return (sc->sc_busdev);
|
|
|
|
}
|
|
|
|
|
2013-02-05 02:25:13 +00:00
|
|
|
static int
|
2017-12-26 12:11:04 +00:00
|
|
|
aw_gpio_pin_max(device_t dev, int *maxpin)
|
2013-02-05 02:25:13 +00:00
|
|
|
{
|
2017-12-26 12:11:04 +00:00
|
|
|
struct aw_gpio_softc *sc;
|
2016-02-17 18:28:03 +00:00
|
|
|
|
|
|
|
sc = device_get_softc(dev);
|
2013-02-05 02:25:13 +00:00
|
|
|
|
2020-01-16 20:02:41 +00:00
|
|
|
*maxpin = sc->conf->padconf->npins - 1;
|
2013-02-05 02:25:13 +00:00
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
2017-12-26 12:11:04 +00:00
|
|
|
aw_gpio_pin_getcaps(device_t dev, uint32_t pin, uint32_t *caps)
|
2013-02-05 02:25:13 +00:00
|
|
|
{
|
2017-12-26 12:11:04 +00:00
|
|
|
struct aw_gpio_softc *sc;
|
2013-02-05 02:25:13 +00:00
|
|
|
|
2016-02-17 18:28:03 +00:00
|
|
|
sc = device_get_softc(dev);
|
2020-01-16 20:02:41 +00:00
|
|
|
if (pin >= sc->conf->padconf->npins)
|
2013-02-05 02:25:13 +00:00
|
|
|
return (EINVAL);
|
|
|
|
|
2017-12-26 12:11:04 +00:00
|
|
|
*caps = AW_GPIO_DEFAULT_CAPS;
|
2020-01-19 19:51:20 +00:00
|
|
|
if (sc->conf->padconf->pins[pin].eint_func != 0)
|
|
|
|
*caps |= AW_GPIO_INTR_CAPS;
|
2013-02-05 02:25:13 +00:00
|
|
|
|
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
2017-12-26 12:11:04 +00:00
|
|
|
aw_gpio_pin_getflags(device_t dev, uint32_t pin, uint32_t *flags)
|
2013-02-05 02:25:13 +00:00
|
|
|
{
|
2017-12-26 12:11:04 +00:00
|
|
|
struct aw_gpio_softc *sc;
|
2016-08-17 10:20:36 +00:00
|
|
|
uint32_t func;
|
|
|
|
uint32_t pud;
|
2013-02-05 02:25:13 +00:00
|
|
|
|
2016-02-17 18:28:03 +00:00
|
|
|
sc = device_get_softc(dev);
|
2020-01-16 20:02:41 +00:00
|
|
|
if (pin >= sc->conf->padconf->npins)
|
2013-02-05 02:25:13 +00:00
|
|
|
return (EINVAL);
|
|
|
|
|
2017-12-26 12:11:04 +00:00
|
|
|
AW_GPIO_LOCK(sc);
|
|
|
|
func = aw_gpio_get_function(sc, pin);
|
2016-08-17 10:20:36 +00:00
|
|
|
switch (func) {
|
2017-12-26 12:11:04 +00:00
|
|
|
case AW_GPIO_INPUT:
|
2016-08-17 10:20:36 +00:00
|
|
|
*flags = GPIO_PIN_INPUT;
|
|
|
|
break;
|
2017-12-26 12:11:04 +00:00
|
|
|
case AW_GPIO_OUTPUT:
|
2016-08-17 10:20:36 +00:00
|
|
|
*flags = GPIO_PIN_OUTPUT;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
*flags = 0;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2017-12-26 12:11:04 +00:00
|
|
|
pud = aw_gpio_get_pud(sc, pin);
|
2016-08-17 10:20:36 +00:00
|
|
|
switch (pud) {
|
2017-12-26 12:11:04 +00:00
|
|
|
case AW_GPIO_PULLDOWN:
|
2016-08-17 10:20:36 +00:00
|
|
|
*flags |= GPIO_PIN_PULLDOWN;
|
|
|
|
break;
|
2017-12-26 12:11:04 +00:00
|
|
|
case AW_GPIO_PULLUP:
|
2016-08-17 10:20:36 +00:00
|
|
|
*flags |= GPIO_PIN_PULLUP;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2017-12-26 12:11:04 +00:00
|
|
|
AW_GPIO_UNLOCK(sc);
|
2013-02-05 02:25:13 +00:00
|
|
|
|
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
2017-12-26 12:11:04 +00:00
|
|
|
aw_gpio_pin_getname(device_t dev, uint32_t pin, char *name)
|
2013-02-05 02:25:13 +00:00
|
|
|
{
|
2017-12-26 12:11:04 +00:00
|
|
|
struct aw_gpio_softc *sc;
|
2013-02-05 02:25:13 +00:00
|
|
|
|
2016-02-17 18:28:03 +00:00
|
|
|
sc = device_get_softc(dev);
|
2020-01-16 20:02:41 +00:00
|
|
|
if (pin >= sc->conf->padconf->npins)
|
2013-02-05 02:25:13 +00:00
|
|
|
return (EINVAL);
|
|
|
|
|
2016-02-17 18:28:03 +00:00
|
|
|
snprintf(name, GPIOMAXNAME - 1, "%s",
|
2020-01-16 20:02:41 +00:00
|
|
|
sc->conf->padconf->pins[pin].name);
|
2015-07-13 18:19:26 +00:00
|
|
|
name[GPIOMAXNAME - 1] = '\0';
|
2013-02-05 02:25:13 +00:00
|
|
|
|
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
2017-12-26 12:11:04 +00:00
|
|
|
aw_gpio_pin_setflags(device_t dev, uint32_t pin, uint32_t flags)
|
2013-02-05 02:25:13 +00:00
|
|
|
{
|
2017-12-26 12:11:04 +00:00
|
|
|
struct aw_gpio_softc *sc;
|
2016-07-08 23:08:59 +00:00
|
|
|
int err;
|
2013-02-05 02:25:13 +00:00
|
|
|
|
2016-02-17 18:28:03 +00:00
|
|
|
sc = device_get_softc(dev);
|
2020-01-16 20:02:41 +00:00
|
|
|
if (pin > sc->conf->padconf->npins)
|
2013-02-05 02:25:13 +00:00
|
|
|
return (EINVAL);
|
|
|
|
|
2017-12-26 12:11:04 +00:00
|
|
|
AW_GPIO_LOCK(sc);
|
|
|
|
err = aw_gpio_pin_configure(sc, pin, flags);
|
|
|
|
AW_GPIO_UNLOCK(sc);
|
2013-02-05 02:25:13 +00:00
|
|
|
|
2016-07-08 23:08:59 +00:00
|
|
|
return (err);
|
2013-02-05 02:25:13 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
2017-12-26 12:11:04 +00:00
|
|
|
aw_gpio_pin_set_locked(struct aw_gpio_softc *sc, uint32_t pin,
|
2017-09-16 14:08:20 +00:00
|
|
|
unsigned int value)
|
2013-02-05 02:25:13 +00:00
|
|
|
{
|
2015-07-13 18:19:26 +00:00
|
|
|
uint32_t bank, data;
|
2013-02-05 02:25:13 +00:00
|
|
|
|
2017-12-26 12:11:04 +00:00
|
|
|
AW_GPIO_LOCK_ASSERT(sc);
|
2017-09-16 14:08:20 +00:00
|
|
|
|
2020-01-16 20:02:41 +00:00
|
|
|
if (pin > sc->conf->padconf->npins)
|
2013-02-05 02:25:13 +00:00
|
|
|
return (EINVAL);
|
|
|
|
|
2020-01-16 20:02:41 +00:00
|
|
|
bank = sc->conf->padconf->pins[pin].port;
|
|
|
|
pin = sc->conf->padconf->pins[pin].pin;
|
2013-02-05 02:25:13 +00:00
|
|
|
|
2017-12-26 12:11:04 +00:00
|
|
|
data = AW_GPIO_READ(sc, AW_GPIO_GP_DAT(bank));
|
2013-02-05 02:25:13 +00:00
|
|
|
if (value)
|
2015-07-13 18:19:26 +00:00
|
|
|
data |= (1 << pin);
|
2013-02-05 02:25:13 +00:00
|
|
|
else
|
2015-07-13 18:19:26 +00:00
|
|
|
data &= ~(1 << pin);
|
2017-12-26 12:11:04 +00:00
|
|
|
AW_GPIO_WRITE(sc, AW_GPIO_GP_DAT(bank), data);
|
2013-02-05 02:25:13 +00:00
|
|
|
|
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
2017-12-26 12:11:04 +00:00
|
|
|
aw_gpio_pin_set(device_t dev, uint32_t pin, unsigned int value)
|
2013-02-05 02:25:13 +00:00
|
|
|
{
|
2017-12-26 12:11:04 +00:00
|
|
|
struct aw_gpio_softc *sc;
|
2017-09-16 14:08:20 +00:00
|
|
|
int ret;
|
2013-02-05 02:25:13 +00:00
|
|
|
|
2016-02-17 18:28:03 +00:00
|
|
|
sc = device_get_softc(dev);
|
2017-09-16 14:08:20 +00:00
|
|
|
|
2017-12-26 12:11:04 +00:00
|
|
|
AW_GPIO_LOCK(sc);
|
|
|
|
ret = aw_gpio_pin_set_locked(sc, pin, value);
|
|
|
|
AW_GPIO_UNLOCK(sc);
|
2017-09-16 14:08:20 +00:00
|
|
|
|
|
|
|
return (ret);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
2017-12-26 12:11:04 +00:00
|
|
|
aw_gpio_pin_get_locked(struct aw_gpio_softc *sc,uint32_t pin,
|
2017-09-16 14:08:20 +00:00
|
|
|
unsigned int *val)
|
|
|
|
{
|
|
|
|
uint32_t bank, reg_data;
|
|
|
|
|
2017-12-26 12:11:04 +00:00
|
|
|
AW_GPIO_LOCK_ASSERT(sc);
|
2017-09-16 14:08:20 +00:00
|
|
|
|
2020-01-16 20:02:41 +00:00
|
|
|
if (pin > sc->conf->padconf->npins)
|
2013-02-05 02:25:13 +00:00
|
|
|
return (EINVAL);
|
|
|
|
|
2020-01-16 20:02:41 +00:00
|
|
|
bank = sc->conf->padconf->pins[pin].port;
|
|
|
|
pin = sc->conf->padconf->pins[pin].pin;
|
2013-02-05 02:25:13 +00:00
|
|
|
|
2017-12-26 12:11:04 +00:00
|
|
|
reg_data = AW_GPIO_READ(sc, AW_GPIO_GP_DAT(bank));
|
2015-07-13 18:19:26 +00:00
|
|
|
*val = (reg_data & (1 << pin)) ? 1 : 0;
|
2013-02-05 02:25:13 +00:00
|
|
|
|
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
|
2017-12-01 20:51:08 +00:00
|
|
|
static char *
|
2017-12-26 12:11:04 +00:00
|
|
|
aw_gpio_parse_function(phandle_t node)
|
2017-12-01 20:51:08 +00:00
|
|
|
{
|
|
|
|
char *function;
|
|
|
|
|
2018-04-08 22:59:34 +00:00
|
|
|
if (OF_getprop_alloc(node, "function",
|
2017-12-01 20:51:08 +00:00
|
|
|
(void **)&function) != -1)
|
|
|
|
return (function);
|
2018-04-08 22:59:34 +00:00
|
|
|
if (OF_getprop_alloc(node, "allwinner,function",
|
2017-12-01 20:51:08 +00:00
|
|
|
(void **)&function) != -1)
|
|
|
|
return (function);
|
|
|
|
|
|
|
|
return (NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
static const char **
|
2017-12-26 12:11:04 +00:00
|
|
|
aw_gpio_parse_pins(phandle_t node, int *pins_nb)
|
2017-12-01 20:51:08 +00:00
|
|
|
{
|
|
|
|
const char **pinlist;
|
|
|
|
|
|
|
|
*pins_nb = ofw_bus_string_list_to_array(node, "pins", &pinlist);
|
|
|
|
if (*pins_nb > 0)
|
|
|
|
return (pinlist);
|
|
|
|
|
|
|
|
*pins_nb = ofw_bus_string_list_to_array(node, "allwinner,pins",
|
|
|
|
&pinlist);
|
|
|
|
if (*pins_nb > 0)
|
|
|
|
return (pinlist);
|
|
|
|
|
|
|
|
return (NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
static uint32_t
|
2017-12-26 12:11:04 +00:00
|
|
|
aw_gpio_parse_bias(phandle_t node)
|
2017-12-01 20:51:08 +00:00
|
|
|
{
|
|
|
|
uint32_t bias;
|
|
|
|
|
|
|
|
if (OF_getencprop(node, "pull", &bias, sizeof(bias)) != -1)
|
|
|
|
return (bias);
|
|
|
|
if (OF_getencprop(node, "allwinner,pull", &bias, sizeof(bias)) != -1)
|
|
|
|
return (bias);
|
|
|
|
if (OF_hasprop(node, "bias-disable"))
|
2017-12-26 12:11:04 +00:00
|
|
|
return (AW_GPIO_NONE);
|
2017-12-01 20:51:08 +00:00
|
|
|
if (OF_hasprop(node, "bias-pull-up"))
|
2017-12-26 12:11:04 +00:00
|
|
|
return (AW_GPIO_PULLUP);
|
2017-12-01 20:51:08 +00:00
|
|
|
if (OF_hasprop(node, "bias-pull-down"))
|
2017-12-26 12:11:04 +00:00
|
|
|
return (AW_GPIO_PULLDOWN);
|
2017-12-01 20:51:08 +00:00
|
|
|
|
2017-12-26 12:11:04 +00:00
|
|
|
return (AW_GPIO_NONE);
|
2017-12-01 20:51:08 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
2017-12-26 12:11:04 +00:00
|
|
|
aw_gpio_parse_drive_strength(phandle_t node, uint32_t *drive)
|
2017-12-01 20:51:08 +00:00
|
|
|
{
|
|
|
|
uint32_t drive_str;
|
|
|
|
|
|
|
|
if (OF_getencprop(node, "drive", drive, sizeof(*drive)) != -1)
|
|
|
|
return (0);
|
|
|
|
if (OF_getencprop(node, "allwinner,drive", drive, sizeof(*drive)) != -1)
|
|
|
|
return (0);
|
|
|
|
if (OF_getencprop(node, "drive-strength", &drive_str,
|
|
|
|
sizeof(drive_str)) != -1) {
|
|
|
|
*drive = (drive_str / 10) - 1;
|
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
|
|
|
|
return (1);
|
|
|
|
}
|
|
|
|
|
2017-09-16 14:08:20 +00:00
|
|
|
static int
|
2017-12-26 12:11:04 +00:00
|
|
|
aw_gpio_pin_get(device_t dev, uint32_t pin, unsigned int *val)
|
2017-09-16 14:08:20 +00:00
|
|
|
{
|
2017-12-26 12:11:04 +00:00
|
|
|
struct aw_gpio_softc *sc;
|
2017-09-16 14:08:20 +00:00
|
|
|
int ret;
|
|
|
|
|
|
|
|
sc = device_get_softc(dev);
|
|
|
|
|
2017-12-26 12:11:04 +00:00
|
|
|
AW_GPIO_LOCK(sc);
|
|
|
|
ret = aw_gpio_pin_get_locked(sc, pin, val);
|
|
|
|
AW_GPIO_UNLOCK(sc);
|
2017-09-16 14:08:20 +00:00
|
|
|
|
|
|
|
return (ret);
|
|
|
|
}
|
|
|
|
|
2013-02-05 02:25:13 +00:00
|
|
|
static int
|
2017-12-26 12:11:04 +00:00
|
|
|
aw_gpio_pin_toggle(device_t dev, uint32_t pin)
|
2013-02-05 02:25:13 +00:00
|
|
|
{
|
2017-12-26 12:11:04 +00:00
|
|
|
struct aw_gpio_softc *sc;
|
2015-07-13 18:19:26 +00:00
|
|
|
uint32_t bank, data;
|
2013-02-05 02:25:13 +00:00
|
|
|
|
2016-02-17 18:28:03 +00:00
|
|
|
sc = device_get_softc(dev);
|
2020-01-16 20:02:41 +00:00
|
|
|
if (pin > sc->conf->padconf->npins)
|
2013-02-05 02:25:13 +00:00
|
|
|
return (EINVAL);
|
|
|
|
|
2020-01-16 20:02:41 +00:00
|
|
|
bank = sc->conf->padconf->pins[pin].port;
|
|
|
|
pin = sc->conf->padconf->pins[pin].pin;
|
2013-02-05 02:25:13 +00:00
|
|
|
|
2017-12-26 12:11:04 +00:00
|
|
|
AW_GPIO_LOCK(sc);
|
|
|
|
data = AW_GPIO_READ(sc, AW_GPIO_GP_DAT(bank));
|
2015-07-13 18:19:26 +00:00
|
|
|
if (data & (1 << pin))
|
|
|
|
data &= ~(1 << pin);
|
2013-02-05 02:25:13 +00:00
|
|
|
else
|
2015-07-13 18:19:26 +00:00
|
|
|
data |= (1 << pin);
|
2017-12-26 12:11:04 +00:00
|
|
|
AW_GPIO_WRITE(sc, AW_GPIO_GP_DAT(bank), data);
|
|
|
|
AW_GPIO_UNLOCK(sc);
|
2013-02-05 02:25:13 +00:00
|
|
|
|
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
|
Add gpio methods to read/write/configure up to 32 pins simultaneously.
Sometimes it is necessary to combine several gpio pins into an ad-hoc bus
and manipulate the pins as a group. In such cases manipulating the pins
individualy is not an option, because the value on the "bus" assumes
potentially-invalid intermediate values as each pin is changed in turn. Note
that the "bus" may be something as simple as a bi-color LED where changing
colors requires changing both gpio pins at once, or something as complex as
a bitbanged multiplexed address/data bus connected to a microcontroller.
In addition to the absolute requirement of simultaneously changing the
output values of driven pins, a desirable feature of these new methods is to
provide a higher-performance mechanism for reading and writing multiple
pins, especially from userland where pin-at-a-time access incurs a noticible
syscall time penalty.
These new interfaces are NOT intended to abstract away all the ugly details
of how gpio is implemented on any given platform. In fact, to use these
properly you absolutely must know something about how the gpio hardware is
organized. Typically there are "banks" of gpio pins controlled by registers
which group several pins together. A bank may be as small as 2 pins or as
big as "all the pins on the device, hundreds of them." In the latter case, a
driver might support this interface by allowing access to any 32 adjacent
pins within the overall collection. Or, more likely, any 32 adjacent pins
starting at any multiple of 32. Whatever the hardware restrictions may be,
you would need to understand them to use this interface.
In additional to defining the interfaces, two example implementations are
included here, for imx5/6, and allwinner. These represent the two primary
types of gpio hardware drivers. imx6 has multiple gpio devices, each
implementing a single bank of 32 pins. Allwinner implements a single large
gpio number space from 1-n pins, and the driver internally translates that
linear number space to a bank+pin scheme based on how the pins are grouped
into control registers. The allwinner implementation imposes the restriction
that the first_pin argument to the new functions must always be pin 0 of a
bank.
Differential Revision: https://reviews.freebsd.org/D11810
2017-09-10 18:08:25 +00:00
|
|
|
static int
|
2017-12-26 12:11:04 +00:00
|
|
|
aw_gpio_pin_access_32(device_t dev, uint32_t first_pin, uint32_t clear_pins,
|
Add gpio methods to read/write/configure up to 32 pins simultaneously.
Sometimes it is necessary to combine several gpio pins into an ad-hoc bus
and manipulate the pins as a group. In such cases manipulating the pins
individualy is not an option, because the value on the "bus" assumes
potentially-invalid intermediate values as each pin is changed in turn. Note
that the "bus" may be something as simple as a bi-color LED where changing
colors requires changing both gpio pins at once, or something as complex as
a bitbanged multiplexed address/data bus connected to a microcontroller.
In addition to the absolute requirement of simultaneously changing the
output values of driven pins, a desirable feature of these new methods is to
provide a higher-performance mechanism for reading and writing multiple
pins, especially from userland where pin-at-a-time access incurs a noticible
syscall time penalty.
These new interfaces are NOT intended to abstract away all the ugly details
of how gpio is implemented on any given platform. In fact, to use these
properly you absolutely must know something about how the gpio hardware is
organized. Typically there are "banks" of gpio pins controlled by registers
which group several pins together. A bank may be as small as 2 pins or as
big as "all the pins on the device, hundreds of them." In the latter case, a
driver might support this interface by allowing access to any 32 adjacent
pins within the overall collection. Or, more likely, any 32 adjacent pins
starting at any multiple of 32. Whatever the hardware restrictions may be,
you would need to understand them to use this interface.
In additional to defining the interfaces, two example implementations are
included here, for imx5/6, and allwinner. These represent the two primary
types of gpio hardware drivers. imx6 has multiple gpio devices, each
implementing a single bank of 32 pins. Allwinner implements a single large
gpio number space from 1-n pins, and the driver internally translates that
linear number space to a bank+pin scheme based on how the pins are grouped
into control registers. The allwinner implementation imposes the restriction
that the first_pin argument to the new functions must always be pin 0 of a
bank.
Differential Revision: https://reviews.freebsd.org/D11810
2017-09-10 18:08:25 +00:00
|
|
|
uint32_t change_pins, uint32_t *orig_pins)
|
|
|
|
{
|
2017-12-26 12:11:04 +00:00
|
|
|
struct aw_gpio_softc *sc;
|
Add gpio methods to read/write/configure up to 32 pins simultaneously.
Sometimes it is necessary to combine several gpio pins into an ad-hoc bus
and manipulate the pins as a group. In such cases manipulating the pins
individualy is not an option, because the value on the "bus" assumes
potentially-invalid intermediate values as each pin is changed in turn. Note
that the "bus" may be something as simple as a bi-color LED where changing
colors requires changing both gpio pins at once, or something as complex as
a bitbanged multiplexed address/data bus connected to a microcontroller.
In addition to the absolute requirement of simultaneously changing the
output values of driven pins, a desirable feature of these new methods is to
provide a higher-performance mechanism for reading and writing multiple
pins, especially from userland where pin-at-a-time access incurs a noticible
syscall time penalty.
These new interfaces are NOT intended to abstract away all the ugly details
of how gpio is implemented on any given platform. In fact, to use these
properly you absolutely must know something about how the gpio hardware is
organized. Typically there are "banks" of gpio pins controlled by registers
which group several pins together. A bank may be as small as 2 pins or as
big as "all the pins on the device, hundreds of them." In the latter case, a
driver might support this interface by allowing access to any 32 adjacent
pins within the overall collection. Or, more likely, any 32 adjacent pins
starting at any multiple of 32. Whatever the hardware restrictions may be,
you would need to understand them to use this interface.
In additional to defining the interfaces, two example implementations are
included here, for imx5/6, and allwinner. These represent the two primary
types of gpio hardware drivers. imx6 has multiple gpio devices, each
implementing a single bank of 32 pins. Allwinner implements a single large
gpio number space from 1-n pins, and the driver internally translates that
linear number space to a bank+pin scheme based on how the pins are grouped
into control registers. The allwinner implementation imposes the restriction
that the first_pin argument to the new functions must always be pin 0 of a
bank.
Differential Revision: https://reviews.freebsd.org/D11810
2017-09-10 18:08:25 +00:00
|
|
|
uint32_t bank, data, pin;
|
|
|
|
|
|
|
|
sc = device_get_softc(dev);
|
2020-01-16 20:02:41 +00:00
|
|
|
if (first_pin > sc->conf->padconf->npins)
|
Add gpio methods to read/write/configure up to 32 pins simultaneously.
Sometimes it is necessary to combine several gpio pins into an ad-hoc bus
and manipulate the pins as a group. In such cases manipulating the pins
individualy is not an option, because the value on the "bus" assumes
potentially-invalid intermediate values as each pin is changed in turn. Note
that the "bus" may be something as simple as a bi-color LED where changing
colors requires changing both gpio pins at once, or something as complex as
a bitbanged multiplexed address/data bus connected to a microcontroller.
In addition to the absolute requirement of simultaneously changing the
output values of driven pins, a desirable feature of these new methods is to
provide a higher-performance mechanism for reading and writing multiple
pins, especially from userland where pin-at-a-time access incurs a noticible
syscall time penalty.
These new interfaces are NOT intended to abstract away all the ugly details
of how gpio is implemented on any given platform. In fact, to use these
properly you absolutely must know something about how the gpio hardware is
organized. Typically there are "banks" of gpio pins controlled by registers
which group several pins together. A bank may be as small as 2 pins or as
big as "all the pins on the device, hundreds of them." In the latter case, a
driver might support this interface by allowing access to any 32 adjacent
pins within the overall collection. Or, more likely, any 32 adjacent pins
starting at any multiple of 32. Whatever the hardware restrictions may be,
you would need to understand them to use this interface.
In additional to defining the interfaces, two example implementations are
included here, for imx5/6, and allwinner. These represent the two primary
types of gpio hardware drivers. imx6 has multiple gpio devices, each
implementing a single bank of 32 pins. Allwinner implements a single large
gpio number space from 1-n pins, and the driver internally translates that
linear number space to a bank+pin scheme based on how the pins are grouped
into control registers. The allwinner implementation imposes the restriction
that the first_pin argument to the new functions must always be pin 0 of a
bank.
Differential Revision: https://reviews.freebsd.org/D11810
2017-09-10 18:08:25 +00:00
|
|
|
return (EINVAL);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* We require that first_pin refers to the first pin in a bank, because
|
|
|
|
* this API is not about convenience, it's for making a set of pins
|
|
|
|
* change simultaneously (required) with reasonably high performance
|
|
|
|
* (desired); we need to do a read-modify-write on a single register.
|
|
|
|
*/
|
2020-01-16 20:02:41 +00:00
|
|
|
bank = sc->conf->padconf->pins[first_pin].port;
|
|
|
|
pin = sc->conf->padconf->pins[first_pin].pin;
|
Add gpio methods to read/write/configure up to 32 pins simultaneously.
Sometimes it is necessary to combine several gpio pins into an ad-hoc bus
and manipulate the pins as a group. In such cases manipulating the pins
individualy is not an option, because the value on the "bus" assumes
potentially-invalid intermediate values as each pin is changed in turn. Note
that the "bus" may be something as simple as a bi-color LED where changing
colors requires changing both gpio pins at once, or something as complex as
a bitbanged multiplexed address/data bus connected to a microcontroller.
In addition to the absolute requirement of simultaneously changing the
output values of driven pins, a desirable feature of these new methods is to
provide a higher-performance mechanism for reading and writing multiple
pins, especially from userland where pin-at-a-time access incurs a noticible
syscall time penalty.
These new interfaces are NOT intended to abstract away all the ugly details
of how gpio is implemented on any given platform. In fact, to use these
properly you absolutely must know something about how the gpio hardware is
organized. Typically there are "banks" of gpio pins controlled by registers
which group several pins together. A bank may be as small as 2 pins or as
big as "all the pins on the device, hundreds of them." In the latter case, a
driver might support this interface by allowing access to any 32 adjacent
pins within the overall collection. Or, more likely, any 32 adjacent pins
starting at any multiple of 32. Whatever the hardware restrictions may be,
you would need to understand them to use this interface.
In additional to defining the interfaces, two example implementations are
included here, for imx5/6, and allwinner. These represent the two primary
types of gpio hardware drivers. imx6 has multiple gpio devices, each
implementing a single bank of 32 pins. Allwinner implements a single large
gpio number space from 1-n pins, and the driver internally translates that
linear number space to a bank+pin scheme based on how the pins are grouped
into control registers. The allwinner implementation imposes the restriction
that the first_pin argument to the new functions must always be pin 0 of a
bank.
Differential Revision: https://reviews.freebsd.org/D11810
2017-09-10 18:08:25 +00:00
|
|
|
if (pin != 0)
|
|
|
|
return (EINVAL);
|
|
|
|
|
2017-12-26 12:11:04 +00:00
|
|
|
AW_GPIO_LOCK(sc);
|
|
|
|
data = AW_GPIO_READ(sc, AW_GPIO_GP_DAT(bank));
|
Add gpio methods to read/write/configure up to 32 pins simultaneously.
Sometimes it is necessary to combine several gpio pins into an ad-hoc bus
and manipulate the pins as a group. In such cases manipulating the pins
individualy is not an option, because the value on the "bus" assumes
potentially-invalid intermediate values as each pin is changed in turn. Note
that the "bus" may be something as simple as a bi-color LED where changing
colors requires changing both gpio pins at once, or something as complex as
a bitbanged multiplexed address/data bus connected to a microcontroller.
In addition to the absolute requirement of simultaneously changing the
output values of driven pins, a desirable feature of these new methods is to
provide a higher-performance mechanism for reading and writing multiple
pins, especially from userland where pin-at-a-time access incurs a noticible
syscall time penalty.
These new interfaces are NOT intended to abstract away all the ugly details
of how gpio is implemented on any given platform. In fact, to use these
properly you absolutely must know something about how the gpio hardware is
organized. Typically there are "banks" of gpio pins controlled by registers
which group several pins together. A bank may be as small as 2 pins or as
big as "all the pins on the device, hundreds of them." In the latter case, a
driver might support this interface by allowing access to any 32 adjacent
pins within the overall collection. Or, more likely, any 32 adjacent pins
starting at any multiple of 32. Whatever the hardware restrictions may be,
you would need to understand them to use this interface.
In additional to defining the interfaces, two example implementations are
included here, for imx5/6, and allwinner. These represent the two primary
types of gpio hardware drivers. imx6 has multiple gpio devices, each
implementing a single bank of 32 pins. Allwinner implements a single large
gpio number space from 1-n pins, and the driver internally translates that
linear number space to a bank+pin scheme based on how the pins are grouped
into control registers. The allwinner implementation imposes the restriction
that the first_pin argument to the new functions must always be pin 0 of a
bank.
Differential Revision: https://reviews.freebsd.org/D11810
2017-09-10 18:08:25 +00:00
|
|
|
if ((clear_pins | change_pins) != 0)
|
2017-12-26 12:11:04 +00:00
|
|
|
AW_GPIO_WRITE(sc, AW_GPIO_GP_DAT(bank),
|
Add gpio methods to read/write/configure up to 32 pins simultaneously.
Sometimes it is necessary to combine several gpio pins into an ad-hoc bus
and manipulate the pins as a group. In such cases manipulating the pins
individualy is not an option, because the value on the "bus" assumes
potentially-invalid intermediate values as each pin is changed in turn. Note
that the "bus" may be something as simple as a bi-color LED where changing
colors requires changing both gpio pins at once, or something as complex as
a bitbanged multiplexed address/data bus connected to a microcontroller.
In addition to the absolute requirement of simultaneously changing the
output values of driven pins, a desirable feature of these new methods is to
provide a higher-performance mechanism for reading and writing multiple
pins, especially from userland where pin-at-a-time access incurs a noticible
syscall time penalty.
These new interfaces are NOT intended to abstract away all the ugly details
of how gpio is implemented on any given platform. In fact, to use these
properly you absolutely must know something about how the gpio hardware is
organized. Typically there are "banks" of gpio pins controlled by registers
which group several pins together. A bank may be as small as 2 pins or as
big as "all the pins on the device, hundreds of them." In the latter case, a
driver might support this interface by allowing access to any 32 adjacent
pins within the overall collection. Or, more likely, any 32 adjacent pins
starting at any multiple of 32. Whatever the hardware restrictions may be,
you would need to understand them to use this interface.
In additional to defining the interfaces, two example implementations are
included here, for imx5/6, and allwinner. These represent the two primary
types of gpio hardware drivers. imx6 has multiple gpio devices, each
implementing a single bank of 32 pins. Allwinner implements a single large
gpio number space from 1-n pins, and the driver internally translates that
linear number space to a bank+pin scheme based on how the pins are grouped
into control registers. The allwinner implementation imposes the restriction
that the first_pin argument to the new functions must always be pin 0 of a
bank.
Differential Revision: https://reviews.freebsd.org/D11810
2017-09-10 18:08:25 +00:00
|
|
|
(data & ~clear_pins) ^ change_pins);
|
2017-12-26 12:11:04 +00:00
|
|
|
AW_GPIO_UNLOCK(sc);
|
Add gpio methods to read/write/configure up to 32 pins simultaneously.
Sometimes it is necessary to combine several gpio pins into an ad-hoc bus
and manipulate the pins as a group. In such cases manipulating the pins
individualy is not an option, because the value on the "bus" assumes
potentially-invalid intermediate values as each pin is changed in turn. Note
that the "bus" may be something as simple as a bi-color LED where changing
colors requires changing both gpio pins at once, or something as complex as
a bitbanged multiplexed address/data bus connected to a microcontroller.
In addition to the absolute requirement of simultaneously changing the
output values of driven pins, a desirable feature of these new methods is to
provide a higher-performance mechanism for reading and writing multiple
pins, especially from userland where pin-at-a-time access incurs a noticible
syscall time penalty.
These new interfaces are NOT intended to abstract away all the ugly details
of how gpio is implemented on any given platform. In fact, to use these
properly you absolutely must know something about how the gpio hardware is
organized. Typically there are "banks" of gpio pins controlled by registers
which group several pins together. A bank may be as small as 2 pins or as
big as "all the pins on the device, hundreds of them." In the latter case, a
driver might support this interface by allowing access to any 32 adjacent
pins within the overall collection. Or, more likely, any 32 adjacent pins
starting at any multiple of 32. Whatever the hardware restrictions may be,
you would need to understand them to use this interface.
In additional to defining the interfaces, two example implementations are
included here, for imx5/6, and allwinner. These represent the two primary
types of gpio hardware drivers. imx6 has multiple gpio devices, each
implementing a single bank of 32 pins. Allwinner implements a single large
gpio number space from 1-n pins, and the driver internally translates that
linear number space to a bank+pin scheme based on how the pins are grouped
into control registers. The allwinner implementation imposes the restriction
that the first_pin argument to the new functions must always be pin 0 of a
bank.
Differential Revision: https://reviews.freebsd.org/D11810
2017-09-10 18:08:25 +00:00
|
|
|
|
|
|
|
if (orig_pins != NULL)
|
|
|
|
*orig_pins = data;
|
|
|
|
|
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
2017-12-26 12:11:04 +00:00
|
|
|
aw_gpio_pin_config_32(device_t dev, uint32_t first_pin, uint32_t num_pins,
|
Add gpio methods to read/write/configure up to 32 pins simultaneously.
Sometimes it is necessary to combine several gpio pins into an ad-hoc bus
and manipulate the pins as a group. In such cases manipulating the pins
individualy is not an option, because the value on the "bus" assumes
potentially-invalid intermediate values as each pin is changed in turn. Note
that the "bus" may be something as simple as a bi-color LED where changing
colors requires changing both gpio pins at once, or something as complex as
a bitbanged multiplexed address/data bus connected to a microcontroller.
In addition to the absolute requirement of simultaneously changing the
output values of driven pins, a desirable feature of these new methods is to
provide a higher-performance mechanism for reading and writing multiple
pins, especially from userland where pin-at-a-time access incurs a noticible
syscall time penalty.
These new interfaces are NOT intended to abstract away all the ugly details
of how gpio is implemented on any given platform. In fact, to use these
properly you absolutely must know something about how the gpio hardware is
organized. Typically there are "banks" of gpio pins controlled by registers
which group several pins together. A bank may be as small as 2 pins or as
big as "all the pins on the device, hundreds of them." In the latter case, a
driver might support this interface by allowing access to any 32 adjacent
pins within the overall collection. Or, more likely, any 32 adjacent pins
starting at any multiple of 32. Whatever the hardware restrictions may be,
you would need to understand them to use this interface.
In additional to defining the interfaces, two example implementations are
included here, for imx5/6, and allwinner. These represent the two primary
types of gpio hardware drivers. imx6 has multiple gpio devices, each
implementing a single bank of 32 pins. Allwinner implements a single large
gpio number space from 1-n pins, and the driver internally translates that
linear number space to a bank+pin scheme based on how the pins are grouped
into control registers. The allwinner implementation imposes the restriction
that the first_pin argument to the new functions must always be pin 0 of a
bank.
Differential Revision: https://reviews.freebsd.org/D11810
2017-09-10 18:08:25 +00:00
|
|
|
uint32_t *pin_flags)
|
|
|
|
{
|
2017-12-26 12:11:04 +00:00
|
|
|
struct aw_gpio_softc *sc;
|
Add gpio methods to read/write/configure up to 32 pins simultaneously.
Sometimes it is necessary to combine several gpio pins into an ad-hoc bus
and manipulate the pins as a group. In such cases manipulating the pins
individualy is not an option, because the value on the "bus" assumes
potentially-invalid intermediate values as each pin is changed in turn. Note
that the "bus" may be something as simple as a bi-color LED where changing
colors requires changing both gpio pins at once, or something as complex as
a bitbanged multiplexed address/data bus connected to a microcontroller.
In addition to the absolute requirement of simultaneously changing the
output values of driven pins, a desirable feature of these new methods is to
provide a higher-performance mechanism for reading and writing multiple
pins, especially from userland where pin-at-a-time access incurs a noticible
syscall time penalty.
These new interfaces are NOT intended to abstract away all the ugly details
of how gpio is implemented on any given platform. In fact, to use these
properly you absolutely must know something about how the gpio hardware is
organized. Typically there are "banks" of gpio pins controlled by registers
which group several pins together. A bank may be as small as 2 pins or as
big as "all the pins on the device, hundreds of them." In the latter case, a
driver might support this interface by allowing access to any 32 adjacent
pins within the overall collection. Or, more likely, any 32 adjacent pins
starting at any multiple of 32. Whatever the hardware restrictions may be,
you would need to understand them to use this interface.
In additional to defining the interfaces, two example implementations are
included here, for imx5/6, and allwinner. These represent the two primary
types of gpio hardware drivers. imx6 has multiple gpio devices, each
implementing a single bank of 32 pins. Allwinner implements a single large
gpio number space from 1-n pins, and the driver internally translates that
linear number space to a bank+pin scheme based on how the pins are grouped
into control registers. The allwinner implementation imposes the restriction
that the first_pin argument to the new functions must always be pin 0 of a
bank.
Differential Revision: https://reviews.freebsd.org/D11810
2017-09-10 18:08:25 +00:00
|
|
|
uint32_t bank, pin;
|
|
|
|
int err;
|
|
|
|
|
|
|
|
sc = device_get_softc(dev);
|
2020-01-16 20:02:41 +00:00
|
|
|
if (first_pin > sc->conf->padconf->npins)
|
Add gpio methods to read/write/configure up to 32 pins simultaneously.
Sometimes it is necessary to combine several gpio pins into an ad-hoc bus
and manipulate the pins as a group. In such cases manipulating the pins
individualy is not an option, because the value on the "bus" assumes
potentially-invalid intermediate values as each pin is changed in turn. Note
that the "bus" may be something as simple as a bi-color LED where changing
colors requires changing both gpio pins at once, or something as complex as
a bitbanged multiplexed address/data bus connected to a microcontroller.
In addition to the absolute requirement of simultaneously changing the
output values of driven pins, a desirable feature of these new methods is to
provide a higher-performance mechanism for reading and writing multiple
pins, especially from userland where pin-at-a-time access incurs a noticible
syscall time penalty.
These new interfaces are NOT intended to abstract away all the ugly details
of how gpio is implemented on any given platform. In fact, to use these
properly you absolutely must know something about how the gpio hardware is
organized. Typically there are "banks" of gpio pins controlled by registers
which group several pins together. A bank may be as small as 2 pins or as
big as "all the pins on the device, hundreds of them." In the latter case, a
driver might support this interface by allowing access to any 32 adjacent
pins within the overall collection. Or, more likely, any 32 adjacent pins
starting at any multiple of 32. Whatever the hardware restrictions may be,
you would need to understand them to use this interface.
In additional to defining the interfaces, two example implementations are
included here, for imx5/6, and allwinner. These represent the two primary
types of gpio hardware drivers. imx6 has multiple gpio devices, each
implementing a single bank of 32 pins. Allwinner implements a single large
gpio number space from 1-n pins, and the driver internally translates that
linear number space to a bank+pin scheme based on how the pins are grouped
into control registers. The allwinner implementation imposes the restriction
that the first_pin argument to the new functions must always be pin 0 of a
bank.
Differential Revision: https://reviews.freebsd.org/D11810
2017-09-10 18:08:25 +00:00
|
|
|
return (EINVAL);
|
|
|
|
|
2020-01-16 20:02:41 +00:00
|
|
|
bank = sc->conf->padconf->pins[first_pin].port;
|
|
|
|
if (sc->conf->padconf->pins[first_pin].pin != 0)
|
Add gpio methods to read/write/configure up to 32 pins simultaneously.
Sometimes it is necessary to combine several gpio pins into an ad-hoc bus
and manipulate the pins as a group. In such cases manipulating the pins
individualy is not an option, because the value on the "bus" assumes
potentially-invalid intermediate values as each pin is changed in turn. Note
that the "bus" may be something as simple as a bi-color LED where changing
colors requires changing both gpio pins at once, or something as complex as
a bitbanged multiplexed address/data bus connected to a microcontroller.
In addition to the absolute requirement of simultaneously changing the
output values of driven pins, a desirable feature of these new methods is to
provide a higher-performance mechanism for reading and writing multiple
pins, especially from userland where pin-at-a-time access incurs a noticible
syscall time penalty.
These new interfaces are NOT intended to abstract away all the ugly details
of how gpio is implemented on any given platform. In fact, to use these
properly you absolutely must know something about how the gpio hardware is
organized. Typically there are "banks" of gpio pins controlled by registers
which group several pins together. A bank may be as small as 2 pins or as
big as "all the pins on the device, hundreds of them." In the latter case, a
driver might support this interface by allowing access to any 32 adjacent
pins within the overall collection. Or, more likely, any 32 adjacent pins
starting at any multiple of 32. Whatever the hardware restrictions may be,
you would need to understand them to use this interface.
In additional to defining the interfaces, two example implementations are
included here, for imx5/6, and allwinner. These represent the two primary
types of gpio hardware drivers. imx6 has multiple gpio devices, each
implementing a single bank of 32 pins. Allwinner implements a single large
gpio number space from 1-n pins, and the driver internally translates that
linear number space to a bank+pin scheme based on how the pins are grouped
into control registers. The allwinner implementation imposes the restriction
that the first_pin argument to the new functions must always be pin 0 of a
bank.
Differential Revision: https://reviews.freebsd.org/D11810
2017-09-10 18:08:25 +00:00
|
|
|
return (EINVAL);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* The configuration for a bank of pins is scattered among several
|
|
|
|
* registers; we cannot g'tee to simultaneously change the state of all
|
|
|
|
* the pins in the flags array. So just loop through the array
|
|
|
|
* configuring each pin for now. If there was a strong need, it might
|
|
|
|
* be possible to support some limited simultaneous config, such as
|
|
|
|
* adjacent groups of 8 pins that line up the same as the config regs.
|
|
|
|
*/
|
|
|
|
for (err = 0, pin = first_pin; err == 0 && pin < num_pins; ++pin) {
|
|
|
|
if (pin_flags[pin] & (GPIO_PIN_INPUT | GPIO_PIN_OUTPUT))
|
2017-12-26 12:11:04 +00:00
|
|
|
err = aw_gpio_pin_configure(sc, pin, pin_flags[pin]);
|
Add gpio methods to read/write/configure up to 32 pins simultaneously.
Sometimes it is necessary to combine several gpio pins into an ad-hoc bus
and manipulate the pins as a group. In such cases manipulating the pins
individualy is not an option, because the value on the "bus" assumes
potentially-invalid intermediate values as each pin is changed in turn. Note
that the "bus" may be something as simple as a bi-color LED where changing
colors requires changing both gpio pins at once, or something as complex as
a bitbanged multiplexed address/data bus connected to a microcontroller.
In addition to the absolute requirement of simultaneously changing the
output values of driven pins, a desirable feature of these new methods is to
provide a higher-performance mechanism for reading and writing multiple
pins, especially from userland where pin-at-a-time access incurs a noticible
syscall time penalty.
These new interfaces are NOT intended to abstract away all the ugly details
of how gpio is implemented on any given platform. In fact, to use these
properly you absolutely must know something about how the gpio hardware is
organized. Typically there are "banks" of gpio pins controlled by registers
which group several pins together. A bank may be as small as 2 pins or as
big as "all the pins on the device, hundreds of them." In the latter case, a
driver might support this interface by allowing access to any 32 adjacent
pins within the overall collection. Or, more likely, any 32 adjacent pins
starting at any multiple of 32. Whatever the hardware restrictions may be,
you would need to understand them to use this interface.
In additional to defining the interfaces, two example implementations are
included here, for imx5/6, and allwinner. These represent the two primary
types of gpio hardware drivers. imx6 has multiple gpio devices, each
implementing a single bank of 32 pins. Allwinner implements a single large
gpio number space from 1-n pins, and the driver internally translates that
linear number space to a bank+pin scheme based on how the pins are grouped
into control registers. The allwinner implementation imposes the restriction
that the first_pin argument to the new functions must always be pin 0 of a
bank.
Differential Revision: https://reviews.freebsd.org/D11810
2017-09-10 18:08:25 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return (err);
|
|
|
|
}
|
|
|
|
|
2020-01-19 19:51:20 +00:00
|
|
|
static int
|
|
|
|
aw_gpio_map_gpios(device_t bus, phandle_t dev, phandle_t gparent, int gcells,
|
|
|
|
pcell_t *gpios, uint32_t *pin, uint32_t *flags)
|
|
|
|
{
|
|
|
|
struct aw_gpio_softc *sc;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
sc = device_get_softc(bus);
|
|
|
|
|
|
|
|
/* The GPIO pins are mapped as: <gpio-phandle bank pin flags>. */
|
|
|
|
for (i = 0; i < sc->conf->padconf->npins; i++)
|
|
|
|
if (sc->conf->padconf->pins[i].port == gpios[0] &&
|
|
|
|
sc->conf->padconf->pins[i].pin == gpios[1]) {
|
|
|
|
*pin = i;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
*flags = gpios[gcells - 1];
|
|
|
|
|
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
|
2016-02-17 18:28:03 +00:00
|
|
|
static int
|
2017-12-26 12:11:04 +00:00
|
|
|
aw_find_pinnum_by_name(struct aw_gpio_softc *sc, const char *pinname)
|
2016-02-17 18:28:03 +00:00
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
2020-01-16 20:02:41 +00:00
|
|
|
for (i = 0; i < sc->conf->padconf->npins; i++)
|
|
|
|
if (!strcmp(pinname, sc->conf->padconf->pins[i].name))
|
2016-02-17 18:28:03 +00:00
|
|
|
return i;
|
|
|
|
|
|
|
|
return (-1);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
2017-12-26 12:11:04 +00:00
|
|
|
aw_find_pin_func(struct aw_gpio_softc *sc, int pin, const char *func)
|
2016-02-17 18:28:03 +00:00
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
for (i = 0; i < AW_MAX_FUNC_BY_PIN; i++)
|
2020-01-16 20:02:41 +00:00
|
|
|
if (sc->conf->padconf->pins[pin].functions[i] &&
|
|
|
|
!strcmp(func, sc->conf->padconf->pins[pin].functions[i]))
|
2016-02-17 18:28:03 +00:00
|
|
|
return (i);
|
|
|
|
|
|
|
|
return (-1);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
aw_fdt_configure_pins(device_t dev, phandle_t cfgxref)
|
|
|
|
{
|
2017-12-26 12:11:04 +00:00
|
|
|
struct aw_gpio_softc *sc;
|
2016-02-17 18:28:03 +00:00
|
|
|
phandle_t node;
|
|
|
|
const char **pinlist = NULL;
|
|
|
|
char *pin_function = NULL;
|
|
|
|
uint32_t pin_drive, pin_pull;
|
|
|
|
int pins_nb, pin_num, pin_func, i, ret;
|
2017-12-01 20:51:08 +00:00
|
|
|
bool set_drive;
|
2016-02-17 18:28:03 +00:00
|
|
|
|
|
|
|
sc = device_get_softc(dev);
|
|
|
|
node = OF_node_from_xref(cfgxref);
|
|
|
|
ret = 0;
|
2017-12-01 20:51:08 +00:00
|
|
|
set_drive = false;
|
2016-02-17 18:28:03 +00:00
|
|
|
|
|
|
|
/* Getting all prop for configuring pins */
|
2017-12-26 12:11:04 +00:00
|
|
|
pinlist = aw_gpio_parse_pins(node, &pins_nb);
|
2017-12-01 20:51:08 +00:00
|
|
|
if (pinlist == NULL)
|
|
|
|
return (ENOENT);
|
|
|
|
|
2017-12-26 12:11:04 +00:00
|
|
|
pin_function = aw_gpio_parse_function(node);
|
2017-12-01 20:51:08 +00:00
|
|
|
if (pin_function == NULL) {
|
|
|
|
ret = ENOENT;
|
|
|
|
goto out;
|
2016-02-17 18:28:03 +00:00
|
|
|
}
|
|
|
|
|
2017-12-26 12:11:04 +00:00
|
|
|
if (aw_gpio_parse_drive_strength(node, &pin_drive) == 0)
|
2017-12-01 20:51:08 +00:00
|
|
|
set_drive = true;
|
|
|
|
|
2017-12-26 12:11:04 +00:00
|
|
|
pin_pull = aw_gpio_parse_bias(node);
|
2017-12-01 20:51:08 +00:00
|
|
|
|
2016-02-17 18:28:03 +00:00
|
|
|
/* Configure each pin to the correct function, drive and pull */
|
|
|
|
for (i = 0; i < pins_nb; i++) {
|
|
|
|
pin_num = aw_find_pinnum_by_name(sc, pinlist[i]);
|
|
|
|
if (pin_num == -1) {
|
|
|
|
ret = ENOENT;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
pin_func = aw_find_pin_func(sc, pin_num, pin_function);
|
|
|
|
if (pin_func == -1) {
|
|
|
|
ret = ENOENT;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
2017-12-26 12:11:04 +00:00
|
|
|
AW_GPIO_LOCK(sc);
|
2016-08-17 10:20:36 +00:00
|
|
|
|
2017-12-26 12:11:04 +00:00
|
|
|
if (aw_gpio_get_function(sc, pin_num) != pin_func)
|
|
|
|
aw_gpio_set_function(sc, pin_num, pin_func);
|
2017-12-01 20:51:08 +00:00
|
|
|
if (set_drive)
|
2017-12-26 12:11:04 +00:00
|
|
|
aw_gpio_set_drv(sc, pin_num, pin_drive);
|
|
|
|
if (pin_pull != AW_GPIO_NONE)
|
|
|
|
aw_gpio_set_pud(sc, pin_num, pin_pull);
|
2017-12-01 20:51:08 +00:00
|
|
|
|
2017-12-26 12:11:04 +00:00
|
|
|
AW_GPIO_UNLOCK(sc);
|
2016-02-17 18:28:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
out:
|
2016-05-13 22:28:02 +00:00
|
|
|
OF_prop_free(pinlist);
|
|
|
|
OF_prop_free(pin_function);
|
2016-02-17 18:28:03 +00:00
|
|
|
return (ret);
|
|
|
|
}
|
|
|
|
|
2020-01-16 20:02:41 +00:00
|
|
|
static void
|
|
|
|
aw_gpio_enable_bank_supply(void *arg)
|
|
|
|
{
|
|
|
|
struct aw_gpio_softc *sc = arg;
|
|
|
|
regulator_t vcc_supply;
|
|
|
|
char bank_reg_name[16];
|
|
|
|
int i, nbanks;
|
|
|
|
|
|
|
|
nbanks = strlen(sc->conf->banks);
|
|
|
|
for (i = 0; i < nbanks; i++) {
|
|
|
|
snprintf(bank_reg_name, sizeof(bank_reg_name), "vcc-p%c-supply",
|
|
|
|
sc->conf->banks[i]);
|
|
|
|
|
|
|
|
if (regulator_get_by_ofw_property(sc->sc_dev, 0, bank_reg_name, &vcc_supply) == 0) {
|
|
|
|
if (bootverbose)
|
|
|
|
device_printf(sc->sc_dev,
|
|
|
|
"Enabling regulator for gpio bank %c\n",
|
|
|
|
sc->conf->banks[i]);
|
|
|
|
if (regulator_enable(vcc_supply) != 0) {
|
|
|
|
device_printf(sc->sc_dev,
|
|
|
|
"Cannot enable regulator for bank %c\n",
|
|
|
|
sc->conf->banks[i]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-02-05 02:25:13 +00:00
|
|
|
static int
|
2017-12-26 12:11:04 +00:00
|
|
|
aw_gpio_probe(device_t dev)
|
2013-02-05 02:25:13 +00:00
|
|
|
{
|
2014-02-02 19:17:28 +00:00
|
|
|
|
|
|
|
if (!ofw_bus_status_okay(dev))
|
|
|
|
return (ENXIO);
|
|
|
|
|
2016-02-10 09:19:29 +00:00
|
|
|
if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0)
|
2013-02-05 02:25:13 +00:00
|
|
|
return (ENXIO);
|
|
|
|
|
2016-02-17 18:28:03 +00:00
|
|
|
device_set_desc(dev, "Allwinner GPIO/Pinmux controller");
|
2013-02-05 02:25:13 +00:00
|
|
|
return (BUS_PROBE_DEFAULT);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
2017-12-26 12:11:04 +00:00
|
|
|
aw_gpio_attach(device_t dev)
|
2013-02-05 02:25:13 +00:00
|
|
|
{
|
2020-01-19 19:51:20 +00:00
|
|
|
int error;
|
2013-02-05 02:25:13 +00:00
|
|
|
phandle_t gpio;
|
2017-12-26 12:11:04 +00:00
|
|
|
struct aw_gpio_softc *sc;
|
2017-09-26 20:23:09 +00:00
|
|
|
struct clk_list *clkp, *clkp_tmp;
|
2016-04-23 13:59:18 +00:00
|
|
|
clk_t clk;
|
2017-09-26 20:23:09 +00:00
|
|
|
hwreset_t rst = NULL;
|
2017-10-02 17:20:07 +00:00
|
|
|
int off, err, clkret;
|
2013-02-05 02:25:13 +00:00
|
|
|
|
2015-07-13 18:19:26 +00:00
|
|
|
sc = device_get_softc(dev);
|
2013-02-05 02:25:13 +00:00
|
|
|
sc->sc_dev = dev;
|
|
|
|
|
2017-12-26 12:11:04 +00:00
|
|
|
mtx_init(&sc->sc_mtx, "aw gpio", "gpio", MTX_SPIN);
|
2013-02-05 02:25:13 +00:00
|
|
|
|
2020-01-19 19:51:20 +00:00
|
|
|
if (bus_alloc_resources(dev, aw_gpio_res_spec, sc->sc_res) != 0) {
|
|
|
|
device_printf(dev, "cannot allocate device resources\n");
|
|
|
|
return (ENXIO);
|
2013-02-05 02:25:13 +00:00
|
|
|
}
|
|
|
|
|
2020-01-19 19:51:20 +00:00
|
|
|
if (bus_setup_intr(dev, sc->sc_res[AW_GPIO_IRQRES],
|
|
|
|
INTR_TYPE_CLK | INTR_MPSAFE, NULL, aw_gpio_intr, sc,
|
|
|
|
&sc->sc_intrhand)) {
|
|
|
|
device_printf(dev, "cannot setup interrupt handler\n");
|
2015-01-31 12:17:07 +00:00
|
|
|
goto fail;
|
2013-02-05 02:25:13 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Find our node. */
|
|
|
|
gpio = ofw_bus_get_node(sc->sc_dev);
|
|
|
|
if (!OF_hasprop(gpio, "gpio-controller"))
|
|
|
|
/* Node is not a GPIO controller. */
|
|
|
|
goto fail;
|
|
|
|
|
2016-02-17 18:28:03 +00:00
|
|
|
/* Use the right pin data for the current SoC */
|
2020-01-16 20:02:41 +00:00
|
|
|
sc->conf = (struct aw_gpio_conf *)ofw_bus_search_compatible(dev,
|
2016-04-23 13:59:18 +00:00
|
|
|
compat_data)->ocd_data;
|
|
|
|
|
2016-07-10 18:28:15 +00:00
|
|
|
if (hwreset_get_by_ofw_idx(dev, 0, 0, &rst) == 0) {
|
2016-04-23 13:59:18 +00:00
|
|
|
error = hwreset_deassert(rst);
|
|
|
|
if (error != 0) {
|
|
|
|
device_printf(dev, "cannot de-assert reset\n");
|
2017-10-02 17:20:07 +00:00
|
|
|
goto fail;
|
2016-04-23 13:59:18 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-09-26 20:23:09 +00:00
|
|
|
TAILQ_INIT(&sc->clk_list);
|
2017-10-02 17:20:07 +00:00
|
|
|
for (off = 0, clkret = 0; clkret == 0; off++) {
|
|
|
|
clkret = clk_get_by_ofw_index(dev, 0, off, &clk);
|
|
|
|
if (clkret != 0)
|
|
|
|
break;
|
2017-09-26 20:23:09 +00:00
|
|
|
err = clk_enable(clk);
|
|
|
|
if (err != 0) {
|
|
|
|
device_printf(dev, "Could not enable clock %s\n",
|
|
|
|
clk_get_name(clk));
|
|
|
|
goto fail;
|
2016-04-23 13:59:18 +00:00
|
|
|
}
|
2017-09-26 20:23:09 +00:00
|
|
|
clkp = malloc(sizeof(*clkp), M_DEVBUF, M_WAITOK | M_ZERO);
|
|
|
|
clkp->clk = clk;
|
|
|
|
TAILQ_INSERT_TAIL(&sc->clk_list, clkp, next);
|
2016-02-17 18:28:03 +00:00
|
|
|
}
|
2017-10-02 17:20:07 +00:00
|
|
|
if (clkret != 0 && clkret != ENOENT) {
|
|
|
|
device_printf(dev, "Could not find clock at offset %d (%d)\n",
|
|
|
|
off, clkret);
|
|
|
|
goto fail;
|
|
|
|
}
|
2016-02-17 18:28:03 +00:00
|
|
|
|
2020-01-19 19:51:20 +00:00
|
|
|
aw_gpio_register_isrcs(sc);
|
|
|
|
intr_pic_register(dev, OF_xref_from_node(ofw_bus_get_node(dev)));
|
|
|
|
|
2016-03-17 08:57:41 +00:00
|
|
|
sc->sc_busdev = gpiobus_attach_bus(dev);
|
|
|
|
if (sc->sc_busdev == NULL)
|
|
|
|
goto fail;
|
|
|
|
|
2016-02-17 18:28:03 +00:00
|
|
|
/*
|
|
|
|
* Register as a pinctrl device
|
|
|
|
*/
|
2017-06-19 06:30:04 +00:00
|
|
|
fdt_pinctrl_register(dev, "pins");
|
|
|
|
fdt_pinctrl_configure_tree(dev);
|
2016-02-17 18:28:03 +00:00
|
|
|
fdt_pinctrl_register(dev, "allwinner,pins");
|
|
|
|
fdt_pinctrl_configure_tree(dev);
|
|
|
|
|
2020-01-16 20:02:41 +00:00
|
|
|
config_intrhook_oneshot(aw_gpio_enable_bank_supply, sc);
|
|
|
|
|
2015-01-31 19:32:14 +00:00
|
|
|
return (0);
|
2013-02-05 02:25:13 +00:00
|
|
|
|
|
|
|
fail:
|
|
|
|
if (sc->sc_irq_res)
|
|
|
|
bus_release_resource(dev, SYS_RES_IRQ, 0, sc->sc_irq_res);
|
|
|
|
if (sc->sc_mem_res)
|
|
|
|
bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->sc_mem_res);
|
2015-01-31 12:17:07 +00:00
|
|
|
mtx_destroy(&sc->sc_mtx);
|
|
|
|
|
2017-09-26 20:23:09 +00:00
|
|
|
/* Disable clock */
|
|
|
|
TAILQ_FOREACH_SAFE(clkp, &sc->clk_list, next, clkp_tmp) {
|
|
|
|
err = clk_disable(clkp->clk);
|
|
|
|
if (err != 0)
|
|
|
|
device_printf(dev, "Could not disable clock %s\n",
|
|
|
|
clk_get_name(clkp->clk));
|
|
|
|
err = clk_release(clkp->clk);
|
|
|
|
if (err != 0)
|
|
|
|
device_printf(dev, "Could not release clock %s\n",
|
|
|
|
clk_get_name(clkp->clk));
|
|
|
|
TAILQ_REMOVE(&sc->clk_list, clkp, next);
|
|
|
|
free(clkp, M_DEVBUF);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Assert resets */
|
|
|
|
if (rst) {
|
|
|
|
hwreset_assert(rst);
|
|
|
|
hwreset_release(rst);
|
|
|
|
}
|
|
|
|
|
2013-02-05 02:25:13 +00:00
|
|
|
return (ENXIO);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
2017-12-26 12:11:04 +00:00
|
|
|
aw_gpio_detach(device_t dev)
|
2013-02-05 02:25:13 +00:00
|
|
|
{
|
|
|
|
|
|
|
|
return (EBUSY);
|
|
|
|
}
|
|
|
|
|
2020-01-19 19:51:20 +00:00
|
|
|
static void
|
|
|
|
aw_gpio_intr(void *arg)
|
2015-07-11 21:09:43 +00:00
|
|
|
{
|
2020-01-19 19:51:20 +00:00
|
|
|
struct aw_gpio_softc *sc;
|
|
|
|
struct intr_irqsrc *isrc;
|
|
|
|
uint32_t reg;
|
|
|
|
int irq;
|
2015-07-11 21:09:43 +00:00
|
|
|
|
2020-01-19 19:51:20 +00:00
|
|
|
sc = (struct aw_gpio_softc *)arg;
|
|
|
|
|
|
|
|
AW_GPIO_LOCK(sc);
|
|
|
|
for (irq = 0; irq < sc->nirqs; irq++) {
|
|
|
|
if (!sc->gpio_pic_irqsrc[irq].enabled)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
reg = AW_GPIO_READ(sc, AW_GPIO_GP_INT_STA(sc->gpio_pic_irqsrc[irq].bank));
|
|
|
|
if (!(reg & (1 << sc->gpio_pic_irqsrc[irq].intnum)))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
isrc = &sc->gpio_pic_irqsrc[irq].isrc;
|
|
|
|
if (intr_isrc_dispatch(isrc, curthread->td_intr_frame) != 0) {
|
|
|
|
aw_gpio_pic_disable_intr_locked(sc, isrc);
|
|
|
|
aw_gpio_pic_post_filter(sc->sc_dev, isrc);
|
|
|
|
device_printf(sc->sc_dev, "Stray irq %u disabled\n", irq);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
AW_GPIO_UNLOCK(sc);
|
2015-07-11 21:09:43 +00:00
|
|
|
}
|
|
|
|
|
2020-01-19 19:51:20 +00:00
|
|
|
/*
|
|
|
|
* Interrupts support
|
|
|
|
*/
|
|
|
|
|
2015-08-30 22:38:06 +00:00
|
|
|
static int
|
2020-01-19 19:51:20 +00:00
|
|
|
aw_gpio_register_isrcs(struct aw_gpio_softc *sc)
|
|
|
|
{
|
|
|
|
const char *name;
|
|
|
|
int nirqs;
|
|
|
|
int pin;
|
|
|
|
int err;
|
|
|
|
|
|
|
|
name = device_get_nameunit(sc->sc_dev);
|
|
|
|
|
|
|
|
for (nirqs = 0, pin = 0; pin < sc->conf->padconf->npins; pin++) {
|
|
|
|
if (sc->conf->padconf->pins[pin].eint_func == 0)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
nirqs++;
|
|
|
|
}
|
|
|
|
|
|
|
|
sc->gpio_pic_irqsrc = malloc(sizeof(*sc->gpio_pic_irqsrc) * nirqs,
|
|
|
|
M_DEVBUF, M_WAITOK | M_ZERO);
|
|
|
|
for (nirqs = 0, pin = 0; pin < sc->conf->padconf->npins; pin++) {
|
|
|
|
if (sc->conf->padconf->pins[pin].eint_func == 0)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
sc->gpio_pic_irqsrc[nirqs].pin = pin;
|
|
|
|
sc->gpio_pic_irqsrc[nirqs].bank = sc->conf->padconf->pins[pin].eint_bank;
|
|
|
|
sc->gpio_pic_irqsrc[nirqs].intnum = sc->conf->padconf->pins[pin].eint_num;
|
|
|
|
sc->gpio_pic_irqsrc[nirqs].intfunc = sc->conf->padconf->pins[pin].eint_func;
|
|
|
|
sc->gpio_pic_irqsrc[nirqs].irq = nirqs;
|
|
|
|
sc->gpio_pic_irqsrc[nirqs].mode = GPIO_INTR_CONFORM;
|
|
|
|
|
|
|
|
err = intr_isrc_register(&sc->gpio_pic_irqsrc[nirqs].isrc,
|
|
|
|
sc->sc_dev, 0, "%s,%s", name,
|
|
|
|
sc->conf->padconf->pins[pin].functions[sc->conf->padconf->pins[pin].eint_func]);
|
|
|
|
if (err) {
|
|
|
|
device_printf(sc->sc_dev, "intr_isrs_register failed for irq %d\n", nirqs);
|
|
|
|
}
|
|
|
|
|
|
|
|
nirqs++;
|
|
|
|
}
|
|
|
|
|
|
|
|
sc->nirqs = nirqs;
|
|
|
|
|
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
aw_gpio_pic_disable_intr_locked(struct aw_gpio_softc *sc, struct intr_irqsrc *isrc)
|
|
|
|
{
|
|
|
|
u_int irq;
|
|
|
|
uint32_t reg;
|
|
|
|
|
|
|
|
AW_GPIO_LOCK_ASSERT(sc);
|
|
|
|
irq = ((struct gpio_irqsrc *)isrc)->irq;
|
|
|
|
reg = AW_GPIO_READ(sc, AW_GPIO_GP_INT_CTL(sc->gpio_pic_irqsrc[irq].bank));
|
|
|
|
reg &= ~(1 << sc->gpio_pic_irqsrc[irq].intnum);
|
|
|
|
AW_GPIO_WRITE(sc, AW_GPIO_GP_INT_CTL(sc->gpio_pic_irqsrc[irq].bank), reg);
|
|
|
|
|
|
|
|
sc->gpio_pic_irqsrc[irq].enabled = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
aw_gpio_pic_disable_intr(device_t dev, struct intr_irqsrc *isrc)
|
2015-08-30 22:38:06 +00:00
|
|
|
{
|
2017-12-26 12:11:04 +00:00
|
|
|
struct aw_gpio_softc *sc;
|
2016-02-17 18:28:03 +00:00
|
|
|
|
2020-01-19 19:51:20 +00:00
|
|
|
sc = device_get_softc(dev);
|
2015-08-30 22:38:06 +00:00
|
|
|
|
2020-01-19 19:51:20 +00:00
|
|
|
AW_GPIO_LOCK(sc);
|
|
|
|
aw_gpio_pic_disable_intr_locked(sc, isrc);
|
|
|
|
AW_GPIO_UNLOCK(sc);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
aw_gpio_pic_enable_intr(device_t dev, struct intr_irqsrc *isrc)
|
|
|
|
{
|
|
|
|
struct aw_gpio_softc *sc;
|
|
|
|
u_int irq;
|
|
|
|
uint32_t reg;
|
|
|
|
|
|
|
|
sc = device_get_softc(dev);
|
|
|
|
irq = ((struct gpio_irqsrc *)isrc)->irq;
|
|
|
|
AW_GPIO_LOCK(sc);
|
|
|
|
reg = AW_GPIO_READ(sc, AW_GPIO_GP_INT_CTL(sc->gpio_pic_irqsrc[irq].bank));
|
|
|
|
reg |= 1 << sc->gpio_pic_irqsrc[irq].intnum;
|
|
|
|
AW_GPIO_WRITE(sc, AW_GPIO_GP_INT_CTL(sc->gpio_pic_irqsrc[irq].bank), reg);
|
|
|
|
AW_GPIO_UNLOCK(sc);
|
|
|
|
|
|
|
|
sc->gpio_pic_irqsrc[irq].enabled = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
aw_gpio_pic_map_gpio(struct aw_gpio_softc *sc, struct intr_map_data_gpio *dag,
|
|
|
|
u_int *irqp, u_int *mode)
|
|
|
|
{
|
|
|
|
u_int irq;
|
|
|
|
int pin;
|
|
|
|
|
|
|
|
irq = dag->gpio_pin_num;
|
|
|
|
|
|
|
|
for (pin = 0; pin < sc->nirqs; pin++)
|
|
|
|
if (sc->gpio_pic_irqsrc[pin].pin == irq)
|
2016-02-17 18:28:03 +00:00
|
|
|
break;
|
2020-01-19 19:51:20 +00:00
|
|
|
if (pin == sc->nirqs) {
|
|
|
|
device_printf(sc->sc_dev, "Invalid interrupt number %u\n", irq);
|
|
|
|
return (EINVAL);
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (dag->gpio_intr_mode) {
|
|
|
|
case GPIO_INTR_LEVEL_LOW:
|
|
|
|
case GPIO_INTR_LEVEL_HIGH:
|
|
|
|
case GPIO_INTR_EDGE_RISING:
|
|
|
|
case GPIO_INTR_EDGE_FALLING:
|
|
|
|
case GPIO_INTR_EDGE_BOTH:
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
device_printf(sc->sc_dev, "Unsupported interrupt mode 0x%8x\n",
|
|
|
|
dag->gpio_intr_mode);
|
|
|
|
return (EINVAL);
|
|
|
|
}
|
|
|
|
|
|
|
|
*irqp = pin;
|
|
|
|
if (mode != NULL)
|
|
|
|
*mode = dag->gpio_intr_mode;
|
|
|
|
|
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
aw_gpio_pic_map_intr(device_t dev, struct intr_map_data *data,
|
|
|
|
struct intr_irqsrc **isrcp)
|
|
|
|
{
|
|
|
|
struct aw_gpio_softc *sc;
|
|
|
|
u_int irq;
|
|
|
|
int err;
|
|
|
|
|
|
|
|
sc = device_get_softc(dev);
|
|
|
|
switch (data->type) {
|
|
|
|
case INTR_MAP_DATA_GPIO:
|
|
|
|
err = aw_gpio_pic_map_gpio(sc,
|
|
|
|
(struct intr_map_data_gpio *)data,
|
|
|
|
&irq, NULL);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
return (ENOTSUP);
|
|
|
|
};
|
|
|
|
|
|
|
|
if (err == 0)
|
|
|
|
*isrcp = &sc->gpio_pic_irqsrc[irq].isrc;
|
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
aw_gpio_pic_setup_intr(device_t dev, struct intr_irqsrc *isrc,
|
|
|
|
struct resource *res, struct intr_map_data *data)
|
|
|
|
{
|
|
|
|
struct aw_gpio_softc *sc;
|
|
|
|
struct gpio_irqsrc *gi;
|
|
|
|
uint32_t irqcfg;
|
|
|
|
uint32_t pinidx, reg;
|
|
|
|
u_int irq, mode;
|
|
|
|
int err;
|
|
|
|
|
|
|
|
sc = device_get_softc(dev);
|
|
|
|
gi = (struct gpio_irqsrc *)isrc;
|
|
|
|
|
|
|
|
switch (data->type) {
|
|
|
|
case INTR_MAP_DATA_GPIO:
|
|
|
|
err = aw_gpio_pic_map_gpio(sc,
|
|
|
|
(struct intr_map_data_gpio *)data,
|
|
|
|
&irq, &mode);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
return (ENOTSUP);
|
|
|
|
};
|
|
|
|
|
|
|
|
pinidx = (sc->gpio_pic_irqsrc[irq].intnum % 8) * 4;
|
|
|
|
|
|
|
|
AW_GPIO_LOCK(sc);
|
|
|
|
switch (mode) {
|
|
|
|
case GPIO_INTR_LEVEL_LOW:
|
|
|
|
irqcfg = AW_GPIO_INT_LEVEL_LOW << pinidx;
|
|
|
|
break;
|
|
|
|
case GPIO_INTR_LEVEL_HIGH:
|
|
|
|
irqcfg = AW_GPIO_INT_LEVEL_HIGH << pinidx;
|
|
|
|
break;
|
|
|
|
case GPIO_INTR_EDGE_RISING:
|
|
|
|
irqcfg = AW_GPIO_INT_EDGE_POSITIVE << pinidx;
|
|
|
|
break;
|
|
|
|
case GPIO_INTR_EDGE_FALLING:
|
|
|
|
irqcfg = AW_GPIO_INT_EDGE_NEGATIVE << pinidx;
|
|
|
|
break;
|
|
|
|
case GPIO_INTR_EDGE_BOTH:
|
|
|
|
irqcfg = AW_GPIO_INT_EDGE_BOTH << pinidx;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Switch the pin to interrupt mode */
|
|
|
|
sc->gpio_pic_irqsrc[irq].oldfunc = aw_gpio_get_function(sc,
|
|
|
|
sc->gpio_pic_irqsrc[irq].pin);
|
|
|
|
aw_gpio_set_function(sc, sc->gpio_pic_irqsrc[irq].pin,
|
|
|
|
sc->gpio_pic_irqsrc[irq].intfunc);
|
|
|
|
|
|
|
|
/* Write interrupt mode */
|
|
|
|
reg = AW_GPIO_READ(sc,
|
|
|
|
AW_GPIO_GP_INT_CFG(sc->gpio_pic_irqsrc[irq].bank,
|
|
|
|
sc->gpio_pic_irqsrc[irq].intnum));
|
|
|
|
reg &= ~(0xF << pinidx);
|
|
|
|
reg |= irqcfg;
|
|
|
|
AW_GPIO_WRITE(sc,
|
|
|
|
AW_GPIO_GP_INT_CFG(sc->gpio_pic_irqsrc[irq].bank,
|
|
|
|
sc->gpio_pic_irqsrc[irq].intnum),
|
|
|
|
reg);
|
|
|
|
|
|
|
|
AW_GPIO_UNLOCK(sc);
|
2015-08-30 22:38:06 +00:00
|
|
|
|
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
|
2020-01-19 19:51:20 +00:00
|
|
|
static int
|
|
|
|
aw_gpio_pic_teardown_intr(device_t dev, struct intr_irqsrc *isrc,
|
|
|
|
struct resource *res, struct intr_map_data *data)
|
|
|
|
{
|
|
|
|
struct aw_gpio_softc *sc;
|
|
|
|
struct gpio_irqsrc *gi;
|
|
|
|
|
|
|
|
sc = device_get_softc(dev);
|
|
|
|
gi = (struct gpio_irqsrc *)isrc;
|
|
|
|
|
|
|
|
/* Switch back the pin to it's original function */
|
|
|
|
AW_GPIO_LOCK(sc);
|
|
|
|
aw_gpio_set_function(sc, gi->pin, gi->oldfunc);
|
|
|
|
AW_GPIO_UNLOCK(sc);
|
|
|
|
|
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
aw_gpio_pic_post_filter(device_t dev, struct intr_irqsrc *isrc)
|
|
|
|
{
|
|
|
|
struct aw_gpio_softc *sc;
|
|
|
|
struct gpio_irqsrc *gi;
|
|
|
|
|
|
|
|
sc = device_get_softc(dev);
|
|
|
|
gi = (struct gpio_irqsrc *)isrc;
|
|
|
|
|
|
|
|
arm_irq_memory_barrier(0);
|
|
|
|
AW_GPIO_WRITE(sc, AW_GPIO_GP_INT_STA(gi->bank), 1 << gi->intnum);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
aw_gpio_pic_post_ithread(device_t dev, struct intr_irqsrc *isrc)
|
|
|
|
{
|
|
|
|
struct aw_gpio_softc *sc;
|
|
|
|
struct gpio_irqsrc *gi;
|
|
|
|
|
|
|
|
sc = device_get_softc(dev);
|
|
|
|
gi = (struct gpio_irqsrc *)isrc;
|
|
|
|
|
|
|
|
arm_irq_memory_barrier(0);
|
|
|
|
AW_GPIO_WRITE(sc, AW_GPIO_GP_INT_STA(gi->bank), 1 << gi->intnum);
|
|
|
|
aw_gpio_pic_enable_intr(dev, isrc);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
aw_gpio_pic_pre_ithread(device_t dev, struct intr_irqsrc *isrc)
|
|
|
|
{
|
|
|
|
struct aw_gpio_softc *sc;
|
|
|
|
|
|
|
|
sc = device_get_softc(dev);
|
|
|
|
aw_gpio_pic_disable_intr_locked(sc, isrc);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* OFWBUS Interface
|
|
|
|
*/
|
|
|
|
static phandle_t
|
|
|
|
aw_gpio_get_node(device_t dev, device_t bus)
|
|
|
|
{
|
|
|
|
|
|
|
|
/* We only have one child, the GPIO bus, which needs our own node. */
|
|
|
|
return (ofw_bus_get_node(dev));
|
|
|
|
}
|
|
|
|
|
2017-12-26 12:11:04 +00:00
|
|
|
static device_method_t aw_gpio_methods[] = {
|
2013-02-05 02:25:13 +00:00
|
|
|
/* Device interface */
|
2017-12-26 12:11:04 +00:00
|
|
|
DEVMETHOD(device_probe, aw_gpio_probe),
|
|
|
|
DEVMETHOD(device_attach, aw_gpio_attach),
|
|
|
|
DEVMETHOD(device_detach, aw_gpio_detach),
|
2013-02-05 02:25:13 +00:00
|
|
|
|
2020-01-19 19:51:20 +00:00
|
|
|
/* Interrupt controller interface */
|
|
|
|
DEVMETHOD(pic_disable_intr, aw_gpio_pic_disable_intr),
|
|
|
|
DEVMETHOD(pic_enable_intr, aw_gpio_pic_enable_intr),
|
|
|
|
DEVMETHOD(pic_map_intr, aw_gpio_pic_map_intr),
|
|
|
|
DEVMETHOD(pic_setup_intr, aw_gpio_pic_setup_intr),
|
|
|
|
DEVMETHOD(pic_teardown_intr, aw_gpio_pic_teardown_intr),
|
|
|
|
DEVMETHOD(pic_post_filter, aw_gpio_pic_post_filter),
|
|
|
|
DEVMETHOD(pic_post_ithread, aw_gpio_pic_post_ithread),
|
|
|
|
DEVMETHOD(pic_pre_ithread, aw_gpio_pic_pre_ithread),
|
|
|
|
|
2013-02-05 02:25:13 +00:00
|
|
|
/* GPIO protocol */
|
2017-12-26 12:11:04 +00:00
|
|
|
DEVMETHOD(gpio_get_bus, aw_gpio_get_bus),
|
|
|
|
DEVMETHOD(gpio_pin_max, aw_gpio_pin_max),
|
|
|
|
DEVMETHOD(gpio_pin_getname, aw_gpio_pin_getname),
|
|
|
|
DEVMETHOD(gpio_pin_getflags, aw_gpio_pin_getflags),
|
|
|
|
DEVMETHOD(gpio_pin_getcaps, aw_gpio_pin_getcaps),
|
|
|
|
DEVMETHOD(gpio_pin_setflags, aw_gpio_pin_setflags),
|
|
|
|
DEVMETHOD(gpio_pin_get, aw_gpio_pin_get),
|
|
|
|
DEVMETHOD(gpio_pin_set, aw_gpio_pin_set),
|
|
|
|
DEVMETHOD(gpio_pin_toggle, aw_gpio_pin_toggle),
|
|
|
|
DEVMETHOD(gpio_pin_access_32, aw_gpio_pin_access_32),
|
|
|
|
DEVMETHOD(gpio_pin_config_32, aw_gpio_pin_config_32),
|
|
|
|
DEVMETHOD(gpio_map_gpios, aw_gpio_map_gpios),
|
2013-02-05 02:25:13 +00:00
|
|
|
|
2015-07-11 21:09:43 +00:00
|
|
|
/* ofw_bus interface */
|
2017-12-26 12:11:04 +00:00
|
|
|
DEVMETHOD(ofw_bus_get_node, aw_gpio_get_node),
|
2015-07-11 21:09:43 +00:00
|
|
|
|
2016-02-17 18:28:03 +00:00
|
|
|
/* fdt_pinctrl interface */
|
|
|
|
DEVMETHOD(fdt_pinctrl_configure,aw_fdt_configure_pins),
|
|
|
|
|
2013-02-05 02:25:13 +00:00
|
|
|
DEVMETHOD_END
|
|
|
|
};
|
|
|
|
|
2017-12-26 12:11:04 +00:00
|
|
|
static devclass_t aw_gpio_devclass;
|
2013-02-05 02:25:13 +00:00
|
|
|
|
2017-12-26 12:11:04 +00:00
|
|
|
static driver_t aw_gpio_driver = {
|
2013-02-05 02:25:13 +00:00
|
|
|
"gpio",
|
2017-12-26 12:11:04 +00:00
|
|
|
aw_gpio_methods,
|
|
|
|
sizeof(struct aw_gpio_softc),
|
2013-02-05 02:25:13 +00:00
|
|
|
};
|
|
|
|
|
2017-12-26 12:11:04 +00:00
|
|
|
EARLY_DRIVER_MODULE(aw_gpio, simplebus, aw_gpio_driver, aw_gpio_devclass, 0, 0,
|
2016-07-21 13:26:39 +00:00
|
|
|
BUS_PASS_INTERRUPT + BUS_PASS_ORDER_LATE);
|