mirror of
https://github.com/Stichting-MINIX-Research-Foundation/netbsd.git
synced 2025-08-12 07:31:12 -04:00
841 lines
23 KiB
C
841 lines
23 KiB
C
/*-
|
|
* Copyright (c) 2013 The NetBSD Foundation, Inc.
|
|
* All rights reserved.
|
|
*
|
|
* This code is derived from software contributed to The NetBSD Foundation
|
|
* by Matt Thomas of 3am Software Foundry.
|
|
*
|
|
* 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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 "locators.h"
|
|
#include "gpio.h"
|
|
|
|
#include "opt_arm_debug.h"
|
|
#include "opt_allwinner.h"
|
|
|
|
#include <sys/cdefs.h>
|
|
|
|
__KERNEL_RCSID(1, "$NetBSD: awin_gpio.c,v 1.20 2015/10/02 16:04:40 bouyer Exp $");
|
|
|
|
#include <sys/param.h>
|
|
#include <sys/bus.h>
|
|
#include <sys/device.h>
|
|
#include <sys/intr.h>
|
|
#include <sys/systm.h>
|
|
#include <sys/kmem.h>
|
|
|
|
#include <sys/gpio.h>
|
|
|
|
#include <dev/gpio/gpiovar.h>
|
|
|
|
#include <arm/allwinner/awin_reg.h>
|
|
#include <arm/allwinner/awin_var.h>
|
|
|
|
static int awin_gpio_match(device_t, cfdata_t, void *);
|
|
static void awin_gpio_attach(device_t, device_t, void *);
|
|
|
|
static int awin_gpio_pin_read(void *, int);
|
|
static void awin_gpio_pin_write(void *, int, int);
|
|
static void awin_gpio_pin_ctl(void *, int, int);
|
|
|
|
#if 0
|
|
static const int ist_maps[] = {
|
|
[IST_LEVEL_LOW] = AWIN_PIO_EINT_LOW_LEVEL,
|
|
[IST_LEVEL_HIGH] = AWIN_PIO_EINT_HIGH_LEVEL,
|
|
[IST_EDGE_FALLING] = AWIN_PIO_EINT_POSITIVE_EDGE,
|
|
[IST_EDGE_RISING] = AWIN_PIO_EINT_NEGATIVE_EDGE,
|
|
[IST_EDGE_BOTH] = AWIN_PIO_EINT_DOUBLE_EDGE,
|
|
};
|
|
#endif
|
|
|
|
struct awin_gpio_pin_cfg {
|
|
uint32_t cfg[4];
|
|
uint32_t drv[2];
|
|
uint32_t pul[2];
|
|
};
|
|
|
|
static struct awin_gpio_pin_group {
|
|
bus_addr_t grp_offset;
|
|
uint32_t grp_pin_mask;
|
|
uint32_t grp_pin_inuse_mask;
|
|
bus_space_handle_t grp_bsh;
|
|
struct awin_gpio_pin_cfg grp_cfg;
|
|
struct gpio_chipset_tag grp_gc_tag;
|
|
const int grp_index;
|
|
const char grp_nc_name[6];
|
|
} pin_groups[] = {
|
|
[0] = {
|
|
.grp_offset = AWIN_PIO_OFFSET + 0 * AWIN_PIO_GRP_SIZE,
|
|
.grp_pin_mask = __BIT(AWIN_PIO_PA_PINS) - 1,
|
|
.grp_gc_tag = {
|
|
.gp_cookie = &pin_groups[0],
|
|
.gp_pin_read = awin_gpio_pin_read,
|
|
.gp_pin_write = awin_gpio_pin_write,
|
|
.gp_pin_ctl = awin_gpio_pin_ctl,
|
|
},
|
|
.grp_index = 0,
|
|
.grp_nc_name = "nc-pa",
|
|
},
|
|
[1] = {
|
|
.grp_offset = AWIN_PIO_OFFSET + 1 * AWIN_PIO_GRP_SIZE,
|
|
.grp_pin_mask = __BIT(AWIN_PIO_PB_PINS) - 1,
|
|
.grp_gc_tag = {
|
|
.gp_cookie = &pin_groups[1],
|
|
.gp_pin_read = awin_gpio_pin_read,
|
|
.gp_pin_write = awin_gpio_pin_write,
|
|
.gp_pin_ctl = awin_gpio_pin_ctl,
|
|
},
|
|
.grp_index = 1,
|
|
.grp_nc_name = "nc-pb",
|
|
},
|
|
[2] = {
|
|
.grp_offset = AWIN_PIO_OFFSET + 2 * AWIN_PIO_GRP_SIZE,
|
|
.grp_pin_mask = __BIT(AWIN_PIO_PC_PINS) - 1,
|
|
.grp_gc_tag = {
|
|
.gp_cookie = &pin_groups[2],
|
|
.gp_pin_read = awin_gpio_pin_read,
|
|
.gp_pin_write = awin_gpio_pin_write,
|
|
.gp_pin_ctl = awin_gpio_pin_ctl,
|
|
},
|
|
.grp_index = 2,
|
|
.grp_nc_name = "nc-pc",
|
|
},
|
|
[3] = {
|
|
.grp_offset = AWIN_PIO_OFFSET + 3 * AWIN_PIO_GRP_SIZE,
|
|
.grp_pin_mask = __BIT(AWIN_PIO_PD_PINS) - 1,
|
|
.grp_gc_tag = {
|
|
.gp_cookie = &pin_groups[3],
|
|
.gp_pin_read = awin_gpio_pin_read,
|
|
.gp_pin_write = awin_gpio_pin_write,
|
|
.gp_pin_ctl = awin_gpio_pin_ctl,
|
|
},
|
|
.grp_index = 3,
|
|
.grp_nc_name = "nc-pd",
|
|
},
|
|
[4] = {
|
|
.grp_offset = AWIN_PIO_OFFSET + 4 * AWIN_PIO_GRP_SIZE,
|
|
.grp_pin_mask = __BIT(AWIN_PIO_PE_PINS) - 1,
|
|
.grp_gc_tag = {
|
|
.gp_cookie = &pin_groups[4],
|
|
.gp_pin_read = awin_gpio_pin_read,
|
|
.gp_pin_write = awin_gpio_pin_write,
|
|
.gp_pin_ctl = awin_gpio_pin_ctl,
|
|
},
|
|
.grp_index = 4,
|
|
.grp_nc_name = "nc-pe",
|
|
},
|
|
[5] = {
|
|
.grp_offset = AWIN_PIO_OFFSET + 5 * AWIN_PIO_GRP_SIZE,
|
|
.grp_pin_mask = __BIT(AWIN_PIO_PF_PINS) - 1,
|
|
.grp_gc_tag = {
|
|
.gp_cookie = &pin_groups[5],
|
|
.gp_pin_read = awin_gpio_pin_read,
|
|
.gp_pin_write = awin_gpio_pin_write,
|
|
.gp_pin_ctl = awin_gpio_pin_ctl,
|
|
},
|
|
.grp_index = 5,
|
|
.grp_nc_name = "nc-pf",
|
|
},
|
|
[6] = {
|
|
.grp_offset = AWIN_PIO_OFFSET + 6 * AWIN_PIO_GRP_SIZE,
|
|
.grp_pin_mask = __BIT(AWIN_PIO_PG_PINS) - 1,
|
|
.grp_gc_tag = {
|
|
.gp_cookie = &pin_groups[6],
|
|
.gp_pin_read = awin_gpio_pin_read,
|
|
.gp_pin_write = awin_gpio_pin_write,
|
|
.gp_pin_ctl = awin_gpio_pin_ctl,
|
|
},
|
|
.grp_index = 6,
|
|
.grp_nc_name = "nc-pg",
|
|
},
|
|
[7] = {
|
|
.grp_offset = AWIN_PIO_OFFSET + 7 * AWIN_PIO_GRP_SIZE,
|
|
.grp_pin_mask = __BIT(AWIN_PIO_PH_PINS) - 1,
|
|
.grp_gc_tag = {
|
|
.gp_cookie = &pin_groups[7],
|
|
.gp_pin_read = awin_gpio_pin_read,
|
|
.gp_pin_write = awin_gpio_pin_write,
|
|
.gp_pin_ctl = awin_gpio_pin_ctl,
|
|
},
|
|
.grp_index = 7,
|
|
.grp_nc_name = "nc-ph",
|
|
},
|
|
[8] = {
|
|
.grp_offset = AWIN_PIO_OFFSET + 8 * AWIN_PIO_GRP_SIZE,
|
|
.grp_pin_mask = __BIT(AWIN_PIO_PI_PINS) - 1,
|
|
.grp_gc_tag = {
|
|
.gp_cookie = &pin_groups[8],
|
|
.gp_pin_read = awin_gpio_pin_read,
|
|
.gp_pin_write = awin_gpio_pin_write,
|
|
.gp_pin_ctl = awin_gpio_pin_ctl,
|
|
},
|
|
.grp_index = 8,
|
|
.grp_nc_name = "nc-pi",
|
|
},
|
|
[9] = {
|
|
.grp_offset = 0,
|
|
.grp_gc_tag = {
|
|
.gp_cookie = &pin_groups[9],
|
|
.gp_pin_read = awin_gpio_pin_read,
|
|
.gp_pin_write = awin_gpio_pin_write,
|
|
.gp_pin_ctl = awin_gpio_pin_ctl,
|
|
},
|
|
.grp_pin_mask = 0,
|
|
.grp_index = 9,
|
|
.grp_nc_name = "nc-pj",
|
|
},
|
|
[10] = {
|
|
.grp_offset = 0,
|
|
.grp_gc_tag = {
|
|
.gp_cookie = &pin_groups[10],
|
|
.gp_pin_read = awin_gpio_pin_read,
|
|
.gp_pin_write = awin_gpio_pin_write,
|
|
.gp_pin_ctl = awin_gpio_pin_ctl,
|
|
},
|
|
.grp_pin_mask = 0,
|
|
.grp_index = 10,
|
|
.grp_nc_name = "nc-pk",
|
|
},
|
|
[11] = {
|
|
.grp_offset = 0,
|
|
.grp_gc_tag = {
|
|
.gp_cookie = &pin_groups[11],
|
|
.gp_pin_read = awin_gpio_pin_read,
|
|
.gp_pin_write = awin_gpio_pin_write,
|
|
.gp_pin_ctl = awin_gpio_pin_ctl,
|
|
},
|
|
.grp_pin_mask = 0,
|
|
.grp_index = 11,
|
|
.grp_nc_name = "nc-pl",
|
|
},
|
|
[12] = {
|
|
.grp_offset = 0,
|
|
.grp_gc_tag = {
|
|
.gp_cookie = &pin_groups[12],
|
|
.gp_pin_read = awin_gpio_pin_read,
|
|
.gp_pin_write = awin_gpio_pin_write,
|
|
.gp_pin_ctl = awin_gpio_pin_ctl,
|
|
},
|
|
.grp_pin_mask = 0,
|
|
.grp_index = 12,
|
|
.grp_nc_name = "nc-pm",
|
|
},
|
|
[13] = {
|
|
.grp_offset = 0,
|
|
.grp_gc_tag = {
|
|
.gp_cookie = &pin_groups[13],
|
|
.gp_pin_read = awin_gpio_pin_read,
|
|
.gp_pin_write = awin_gpio_pin_write,
|
|
.gp_pin_ctl = awin_gpio_pin_ctl,
|
|
},
|
|
.grp_pin_mask = 0,
|
|
.grp_nc_name = "nc-pn",
|
|
},
|
|
};
|
|
|
|
|
|
static struct awin_gpio_softc {
|
|
device_t sc_dev;
|
|
bus_space_tag_t sc_bst;
|
|
bus_space_handle_t sc_bsh;
|
|
} awin_gpio_sc = {
|
|
.sc_bst = &armv7_generic_bs_tag,
|
|
};
|
|
|
|
CFATTACH_DECL_NEW(awin_gpio, sizeof(struct awin_gpio_softc),
|
|
awin_gpio_match, awin_gpio_attach, NULL, NULL);
|
|
|
|
static int
|
|
awin_gpio_match(device_t parent, cfdata_t cf, void *aux)
|
|
{
|
|
struct awinio_attach_args * const aio __diagused = aux;
|
|
const struct awin_locators * const loc __diagused = &aio->aio_loc;
|
|
|
|
KASSERT(!strcmp(cf->cf_name, loc->loc_name));
|
|
KASSERT(loc->loc_port == AWINIOCF_PORT_DEFAULT);
|
|
KASSERT(cf->cf_loc[AWINIOCF_PORT] == AWINIOCF_PORT_DEFAULT);
|
|
|
|
if (awin_gpio_sc.sc_dev != NULL)
|
|
return 0;
|
|
|
|
return 1;
|
|
}
|
|
|
|
#if NGPIO > 0
|
|
static int
|
|
awin_gpio_cfprint(void *priv, const char *pnp)
|
|
{
|
|
struct gpiobus_attach_args *gba = priv;
|
|
struct awin_gpio_pin_group *grp = gba->gba_gc->gp_cookie;
|
|
|
|
if (pnp)
|
|
aprint_normal("gpiobus at %s", pnp);
|
|
|
|
aprint_normal(" port %c", 'A' + grp->grp_index);
|
|
|
|
return UNCONF;
|
|
}
|
|
|
|
static void
|
|
awin_gpio_config_pins(device_t self)
|
|
{
|
|
struct awin_gpio_softc * const sc = &awin_gpio_sc;
|
|
|
|
/*
|
|
* First find out how many pins we can offer.
|
|
*/
|
|
size_t pin_count = 0;
|
|
for (u_int i = 0; i < __arraycount(pin_groups); i++) {
|
|
struct awin_gpio_pin_group * const grp = &pin_groups[i];
|
|
|
|
pin_count +=
|
|
popcount32(grp->grp_pin_mask & ~grp->grp_pin_inuse_mask);
|
|
}
|
|
|
|
/*
|
|
* Allocate the pin data.
|
|
*/
|
|
gpio_pin_t * const pins = kmem_zalloc(sizeof(gpio_pin_t) * pin_count,
|
|
KM_SLEEP);
|
|
KASSERT(pins != NULL);
|
|
|
|
const int pincaps = GPIO_PIN_INPUT | GPIO_PIN_OUTPUT
|
|
| GPIO_PIN_PULLUP | GPIO_PIN_PULLDOWN;
|
|
|
|
gpio_pin_t *pin = pins;
|
|
for (u_int i = 0; i < __arraycount(pin_groups); i++) {
|
|
struct awin_gpio_pin_group * const grp = &pin_groups[i];
|
|
uint32_t mask = grp->grp_pin_mask & ~grp->grp_pin_inuse_mask;
|
|
|
|
/*
|
|
* If this group has no bits to provide, skip it.
|
|
*/
|
|
if (mask == 0)
|
|
continue;
|
|
|
|
struct gpiobus_attach_args gba = {
|
|
.gba_gc = &grp->grp_gc_tag,
|
|
.gba_pins = pin,
|
|
};
|
|
|
|
uint32_t data = bus_space_read_4(sc->sc_bst, grp->grp_bsh,
|
|
AWIN_PIO_DAT_REG);
|
|
for (int num = 0; mask != 0; mask >>= 1, data >>= 1, num++) {
|
|
if (mask & 1) {
|
|
pin->pin_num = num + (i << 5);
|
|
pin->pin_caps = pincaps;
|
|
pin->pin_flags = pincaps;
|
|
pin->pin_state = (data & 1) != 0;
|
|
pin++;
|
|
}
|
|
}
|
|
|
|
gba.gba_npins = pin - gba.gba_pins;
|
|
config_found_ia(self, "gpiobus", &gba, awin_gpio_cfprint);
|
|
}
|
|
}
|
|
#endif /* NGPIO > 0 */
|
|
|
|
static void
|
|
awin_gpio_attach(device_t parent, device_t self, void *aux)
|
|
{
|
|
struct awin_gpio_softc * const sc = &awin_gpio_sc;
|
|
struct awinio_attach_args * const aio = aux;
|
|
const struct awin_locators * const loc = &aio->aio_loc;
|
|
prop_dictionary_t dict = device_properties(self);
|
|
|
|
sc->sc_dev = self;
|
|
|
|
sc->sc_bst = aio->aio_core_bst;
|
|
bus_space_subregion(sc->sc_bst, aio->aio_core_bsh,
|
|
loc->loc_offset, loc->loc_size, &sc->sc_bsh);
|
|
|
|
aprint_naive("\n");
|
|
aprint_normal("\n");
|
|
|
|
for (u_int i = 0; i < __arraycount(pin_groups); i++) {
|
|
struct awin_gpio_pin_group * const grp = &pin_groups[i];
|
|
|
|
/*
|
|
* See if this group has any unconnected pins and make sure
|
|
* we won't use them.
|
|
*/
|
|
uint32_t nc;
|
|
if (prop_dictionary_get_uint32(dict, grp->grp_nc_name, &nc)) {
|
|
KASSERT((~grp->grp_pin_mask & nc) == 0);
|
|
KASSERT((grp->grp_pin_inuse_mask & ~nc) == 0);
|
|
grp->grp_pin_mask &= ~nc;
|
|
}
|
|
}
|
|
|
|
#if NGPIO > 0
|
|
config_defer(self, awin_gpio_config_pins);
|
|
#endif
|
|
}
|
|
|
|
static u_int
|
|
awin_gpio_get_pin_func(const struct awin_gpio_pin_cfg *cfg, u_int pin)
|
|
{
|
|
const u_int shift = (pin & 7) << 2;
|
|
const u_int i = (pin >> 3) & 3;
|
|
|
|
return (cfg->cfg[i] >> shift) & 0x0f;
|
|
}
|
|
|
|
static void
|
|
awin_gpio_set_pin_func(struct awin_gpio_pin_cfg *cfg, u_int pin, u_int func)
|
|
{
|
|
const u_int shift = (pin & 7) << 2;
|
|
const u_int i = (pin >> 3) & 3;
|
|
|
|
cfg->cfg[i] &= ~(0x0f << shift);
|
|
cfg->cfg[i] |= func << shift;
|
|
}
|
|
|
|
static void
|
|
awin_gpio_set_pin_pull(struct awin_gpio_pin_cfg *cfg, u_int pin, u_int pull)
|
|
{
|
|
const u_int shift = (pin & 15) << 1;
|
|
const u_int i = (pin >> 4) & 1;
|
|
|
|
cfg->pul[i] &= ~(0x03 << shift);
|
|
cfg->pul[i] |= pull << shift;
|
|
}
|
|
|
|
static void
|
|
awin_gpio_set_pin_drv(struct awin_gpio_pin_cfg *cfg, u_int pin, u_int drv)
|
|
{
|
|
const u_int shift = (pin & 15) << 1;
|
|
const u_int i = (pin >> 4) & 1;
|
|
|
|
cfg->drv[i] &= ~(0x03 << shift);
|
|
cfg->drv[i] |= drv << shift;
|
|
}
|
|
|
|
static void
|
|
awin_gpio_update_cfg_regs(bus_space_tag_t bst, struct awin_gpio_pin_group *grp,
|
|
const struct awin_gpio_pin_cfg *ncfg)
|
|
{
|
|
for (u_int i = 0; i < 4; i++) {
|
|
if (grp->grp_cfg.cfg[i] != ncfg->cfg[i]) {
|
|
bus_space_write_4(bst, grp->grp_bsh,
|
|
AWIN_PIO_CFG0_REG + 4 * i, ncfg->cfg[i]);
|
|
grp->grp_cfg.cfg[i] = ncfg->cfg[i];
|
|
}
|
|
}
|
|
for (u_int i = 0; i < 2; i++) {
|
|
if (grp->grp_cfg.drv[i] != ncfg->drv[i]) {
|
|
bus_space_write_4(bst, grp->grp_bsh,
|
|
AWIN_PIO_DRV0_REG + 4 * i, ncfg->drv[i]);
|
|
grp->grp_cfg.drv[i] = ncfg->drv[i];
|
|
}
|
|
if (grp->grp_cfg.pul[i] != ncfg->pul[i]) {
|
|
bus_space_write_4(bst, grp->grp_bsh,
|
|
AWIN_PIO_PUL0_REG + 4 * i, ncfg->pul[i]);
|
|
grp->grp_cfg.pul[i] = ncfg->pul[i];
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
awin_gpio_init(void)
|
|
{
|
|
struct awin_gpio_softc * const sc = &awin_gpio_sc;
|
|
|
|
#ifdef VERBOSE_INIT_ARM
|
|
printf(" free");
|
|
#endif
|
|
|
|
if (awin_chip_id() == AWIN_CHIP_ID_A31) {
|
|
pin_groups[0].grp_pin_mask = __BIT(AWIN_A31_PIO_PA_PINS) - 1;
|
|
pin_groups[1].grp_pin_mask = __BIT(AWIN_A31_PIO_PB_PINS) - 1;
|
|
pin_groups[2].grp_pin_mask = __BIT(AWIN_A31_PIO_PC_PINS) - 1;
|
|
pin_groups[3].grp_pin_mask = __BIT(AWIN_A31_PIO_PD_PINS) - 1;
|
|
pin_groups[4].grp_pin_mask = __BIT(AWIN_A31_PIO_PE_PINS) - 1;
|
|
pin_groups[5].grp_pin_mask = __BIT(AWIN_A31_PIO_PF_PINS) - 1;
|
|
pin_groups[6].grp_pin_mask = __BIT(AWIN_A31_PIO_PG_PINS) - 1;
|
|
pin_groups[7].grp_pin_mask = __BIT(AWIN_A31_PIO_PH_PINS) - 1;
|
|
pin_groups[8].grp_offset = 0; /* PI */
|
|
pin_groups[8].grp_pin_mask = 0; /* PI */
|
|
pin_groups[9].grp_offset = 0; /* PJ */
|
|
pin_groups[9].grp_pin_mask = 0; /* PJ */
|
|
pin_groups[10].grp_offset = 0; /* PK */
|
|
pin_groups[10].grp_pin_mask = 0; /* PK */
|
|
pin_groups[11].grp_offset = AWIN_A31_CPUPIO_OFFSET +
|
|
0 * AWIN_PIO_GRP_SIZE;
|
|
pin_groups[11].grp_pin_mask = __BIT(AWIN_A31_PIO_PL_PINS) - 1;
|
|
pin_groups[12].grp_offset = AWIN_A31_CPUPIO_OFFSET +
|
|
1 * AWIN_PIO_GRP_SIZE;
|
|
pin_groups[12].grp_pin_mask = __BIT(AWIN_A31_PIO_PM_PINS) - 1;
|
|
pin_groups[13].grp_offset = 0; /* PN */
|
|
pin_groups[13].grp_pin_mask = 0; /* PN */
|
|
} else if (awin_chip_id() == AWIN_CHIP_ID_A80) {
|
|
pin_groups[0].grp_pin_mask = __BIT(AWIN_A80_PIO_PA_PINS) - 1;
|
|
pin_groups[0].grp_offset = AWIN_A80_PIO_OFFSET +
|
|
0 * AWIN_PIO_GRP_SIZE;
|
|
pin_groups[1].grp_pin_mask = __BIT(AWIN_A80_PIO_PB_PINS) - 1;
|
|
pin_groups[1].grp_offset = AWIN_A80_PIO_OFFSET +
|
|
1 * AWIN_PIO_GRP_SIZE;
|
|
pin_groups[2].grp_pin_mask = __BIT(AWIN_A80_PIO_PC_PINS) - 1;
|
|
pin_groups[2].grp_offset = AWIN_A80_PIO_OFFSET +
|
|
2 * AWIN_PIO_GRP_SIZE;
|
|
pin_groups[3].grp_pin_mask = __BIT(AWIN_A80_PIO_PD_PINS) - 1;
|
|
pin_groups[3].grp_offset = AWIN_A80_PIO_OFFSET +
|
|
3 * AWIN_PIO_GRP_SIZE;
|
|
pin_groups[4].grp_pin_mask = __BIT(AWIN_A80_PIO_PE_PINS) - 1;
|
|
pin_groups[4].grp_offset = AWIN_A80_PIO_OFFSET +
|
|
4 * AWIN_PIO_GRP_SIZE;
|
|
pin_groups[5].grp_pin_mask = __BIT(AWIN_A80_PIO_PF_PINS) - 1;
|
|
pin_groups[5].grp_offset = AWIN_A80_PIO_OFFSET +
|
|
5 * AWIN_PIO_GRP_SIZE;
|
|
pin_groups[6].grp_pin_mask = __BIT(AWIN_A80_PIO_PG_PINS) - 1;
|
|
pin_groups[6].grp_offset = AWIN_A80_PIO_OFFSET +
|
|
6 * AWIN_PIO_GRP_SIZE;
|
|
pin_groups[7].grp_pin_mask = __BIT(AWIN_A80_PIO_PH_PINS) - 1;
|
|
pin_groups[7].grp_offset = AWIN_A80_PIO_OFFSET +
|
|
7 * AWIN_PIO_GRP_SIZE;
|
|
pin_groups[8].grp_offset = 0; /* PI */
|
|
pin_groups[8].grp_pin_mask = 0; /* PI */
|
|
pin_groups[9].grp_offset = 0; /* PJ */
|
|
pin_groups[9].grp_pin_mask = 0; /* PJ */
|
|
pin_groups[10].grp_offset = 0; /* PK */
|
|
pin_groups[10].grp_pin_mask = 0; /* PK */
|
|
pin_groups[11].grp_offset = AWIN_A80_RPIO_OFFSET +
|
|
0 * AWIN_PIO_GRP_SIZE;
|
|
pin_groups[11].grp_pin_mask = __BIT(AWIN_A80_PIO_PL_PINS) - 1;
|
|
pin_groups[12].grp_offset = AWIN_A80_RPIO_OFFSET +
|
|
1 * AWIN_PIO_GRP_SIZE;
|
|
pin_groups[12].grp_pin_mask = __BIT(AWIN_A80_PIO_PM_PINS) - 1;
|
|
pin_groups[13].grp_offset = AWIN_A80_RPIO_OFFSET +
|
|
2 * AWIN_PIO_GRP_SIZE;
|
|
pin_groups[13].grp_pin_mask = __BIT(AWIN_A80_PIO_PN_PINS) - 1;
|
|
}
|
|
|
|
for (u_int i = 0; i < __arraycount(pin_groups); i++) {
|
|
struct awin_gpio_pin_group * const grp = &pin_groups[i];
|
|
|
|
if (grp->grp_offset == 0)
|
|
continue;
|
|
|
|
#if defined(ALLWINNER_A80)
|
|
if (i >= 11) {
|
|
bus_space_subregion(sc->sc_bst, awin_rcpus_bsh,
|
|
grp->grp_offset, AWIN_PIO_GRP_SIZE, &grp->grp_bsh);
|
|
} else
|
|
#endif
|
|
{
|
|
bus_space_subregion(sc->sc_bst, awin_core_bsh,
|
|
grp->grp_offset, AWIN_PIO_GRP_SIZE, &grp->grp_bsh);
|
|
}
|
|
|
|
for (u_int j = 0; j < 4; j++) {
|
|
grp->grp_cfg.cfg[j] = bus_space_read_4(sc->sc_bst,
|
|
grp->grp_bsh, AWIN_PIO_CFG0_REG + j * 4);
|
|
}
|
|
grp->grp_cfg.drv[0] = bus_space_read_4(sc->sc_bst,
|
|
grp->grp_bsh, AWIN_PIO_DRV0_REG);
|
|
grp->grp_cfg.drv[1] = bus_space_read_4(sc->sc_bst,
|
|
grp->grp_bsh, AWIN_PIO_DRV1_REG);
|
|
grp->grp_cfg.pul[0] = bus_space_read_4(sc->sc_bst,
|
|
grp->grp_bsh, AWIN_PIO_PUL0_REG);
|
|
grp->grp_cfg.pul[1] = bus_space_read_4(sc->sc_bst,
|
|
grp->grp_bsh, AWIN_PIO_PUL1_REG);
|
|
|
|
#if !defined(AWIN_GPIO_IGNORE_FW)
|
|
for (uint32_t j = 0, mask = 1;
|
|
(mask & grp->grp_pin_mask) != 0;
|
|
j++, mask <<= 1) {
|
|
u_int func = awin_gpio_get_pin_func(&grp->grp_cfg, j);
|
|
if (func > AWIN_PIO_FUNC_OUTPUT) {
|
|
grp->grp_pin_inuse_mask |= mask;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
#ifdef VERBOSE_INIT_ARM
|
|
printf(" P%c=%d", 'A' + i,
|
|
popcount32(grp->grp_pin_mask & ~grp->grp_pin_inuse_mask));
|
|
#endif
|
|
}
|
|
}
|
|
|
|
bool
|
|
awin_gpio_pinset_available(const struct awin_gpio_pinset *req)
|
|
{
|
|
KASSERT(req != NULL);
|
|
|
|
if (!req->pinset_group)
|
|
return false;
|
|
|
|
#ifdef DIAGNOSTIC
|
|
if (awin_chip_id() == AWIN_CHIP_ID_A31) {
|
|
KASSERT(
|
|
('A' <= req->pinset_group && req->pinset_group <= 'I') ||
|
|
('L' <= req->pinset_group && req->pinset_group <= 'M'));
|
|
} else if (awin_chip_id() == AWIN_CHIP_ID_A80) {
|
|
KASSERT(
|
|
('A' <= req->pinset_group && req->pinset_group <= 'I') ||
|
|
('L' <= req->pinset_group && req->pinset_group <= 'N'));
|
|
} else {
|
|
KASSERT('A' <= req->pinset_group && req->pinset_group <= 'I');
|
|
}
|
|
#endif
|
|
|
|
struct awin_gpio_pin_group * const grp =
|
|
&pin_groups[req->pinset_group - 'A'];
|
|
|
|
/*
|
|
* If there are unconnected pins, then they've been remove from
|
|
* the groups pin mask. If we want pins that are unconnected,
|
|
* fail the request.
|
|
*/
|
|
if (req->pinset_mask & ~grp->grp_pin_mask)
|
|
return false;
|
|
|
|
/*
|
|
* If none of the pins are in use, they must be available.
|
|
*/
|
|
if (req->pinset_mask & ~grp->grp_pin_inuse_mask)
|
|
return true;
|
|
|
|
/*
|
|
* Check to see if the pins are already setup for this function.
|
|
*/
|
|
for (uint32_t j = 0, inuse = req->pinset_mask & grp->grp_pin_inuse_mask;
|
|
inuse != 0;
|
|
j++, inuse >>= 1) {
|
|
const u_int n = ffs(inuse) - 1;
|
|
j += n;
|
|
inuse >>= n;
|
|
/*
|
|
* If this pin is in use but it's for a different
|
|
* function, fail the request.
|
|
*/
|
|
if (awin_gpio_get_pin_func(&grp->grp_cfg, j) != req->pinset_func)
|
|
return false;
|
|
}
|
|
|
|
/*
|
|
* Nothing incompatible encountered so the pins must be available.
|
|
*/
|
|
return true;
|
|
}
|
|
|
|
void
|
|
awin_gpio_pinset_acquire(const struct awin_gpio_pinset *req)
|
|
{
|
|
KASSERT(awin_gpio_pinset_available(req));
|
|
|
|
struct awin_gpio_pin_group * const grp =
|
|
&pin_groups[req->pinset_group - 'A'];
|
|
|
|
|
|
/*
|
|
* If all the pins already have right function, just return.
|
|
*/
|
|
if ((req->pinset_mask & ~grp->grp_pin_inuse_mask) == 0) {
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Copy the current config.
|
|
*/
|
|
struct awin_gpio_pin_cfg ncfg = grp->grp_cfg;
|
|
|
|
/*
|
|
* For each pin not inuse, update the cloned config's function for it.
|
|
*/
|
|
for (uint32_t j = 0, todo = req->pinset_mask & ~grp->grp_pin_inuse_mask;
|
|
todo != 0;
|
|
j++, todo >>= 1) {
|
|
const u_int n = ffs(todo) - 1;
|
|
j += n;
|
|
todo >>= n;
|
|
/*
|
|
* Change the function of this pin.
|
|
*/
|
|
awin_gpio_set_pin_func(&ncfg, j, req->pinset_func);
|
|
|
|
if (req->pinset_flags & GPIO_PIN_PULLDOWN)
|
|
awin_gpio_set_pin_pull(&ncfg, j, AWIN_PIO_PULL_DOWN);
|
|
else if (req->pinset_flags & GPIO_PIN_PULLUP)
|
|
awin_gpio_set_pin_pull(&ncfg, j, AWIN_PIO_PULL_UP);
|
|
|
|
if (req->pinset_drv)
|
|
awin_gpio_set_pin_drv(&ncfg, j, req->pinset_drv);
|
|
}
|
|
|
|
/*
|
|
* Now update any config register that changed.
|
|
*/
|
|
awin_gpio_update_cfg_regs(&armv7_generic_bs_tag, grp, &ncfg);
|
|
|
|
/*
|
|
* Mark all these pins as in use.
|
|
*/
|
|
grp->grp_pin_inuse_mask |= req->pinset_mask;
|
|
}
|
|
|
|
void
|
|
awin_gpio_pinset_release(const struct awin_gpio_pinset *req)
|
|
{
|
|
KASSERT(awin_gpio_pinset_available(req));
|
|
|
|
struct awin_gpio_pin_group * const grp =
|
|
&pin_groups[req->pinset_group - 'A'];
|
|
|
|
#if 0
|
|
/*
|
|
* Copy the current config.
|
|
*/
|
|
struct awin_gpio_pin_cfg ncfg = grp->grp_cfg;
|
|
|
|
/*
|
|
* For each pin not inuse, update the cloned config's function for it.
|
|
*/
|
|
for (uint32_t j = 0, todo = req->pinset_mask;
|
|
todo != 0;
|
|
j++, todo >>= 1) {
|
|
const u_int n = ffs(todo) - 1;
|
|
j += n;
|
|
todo >>= n;
|
|
/*
|
|
* Change the function of this pin.
|
|
*/
|
|
awin_gpio_set_pin_func(&ncfg, AWIN_PIO_FUNC_INPUT);
|
|
}
|
|
|
|
/*
|
|
* Now update any config register that changed.
|
|
*/
|
|
awin_gpio_update_cfg_regs(sc->sc_bst, grp, &ncfg);
|
|
#endif
|
|
|
|
/*
|
|
* Clear these pins as being in use.
|
|
*/
|
|
grp->grp_pin_inuse_mask &= ~req->pinset_mask;
|
|
}
|
|
|
|
static int
|
|
awin_gpio_pin_read(void *cookie, int pin)
|
|
{
|
|
struct awin_gpio_pin_group * const grp = cookie;
|
|
|
|
KASSERT(pin < 32);
|
|
|
|
return (bus_space_read_4(awin_gpio_sc.sc_bst, grp->grp_bsh,
|
|
AWIN_PIO_DAT_REG) >> pin) & 1;
|
|
}
|
|
|
|
static void
|
|
awin_gpio_pin_write(void *cookie, int pin, int value)
|
|
{
|
|
struct awin_gpio_pin_group * const grp = cookie;
|
|
|
|
KASSERT(pin < 32);
|
|
|
|
awin_reg_set_clear(awin_gpio_sc.sc_bst, grp->grp_bsh,
|
|
AWIN_PIO_DAT_REG, value ? __BIT(pin) : 0, __BIT(pin));
|
|
}
|
|
|
|
static void
|
|
awin_gpio_pin_ctl(void *cookie, int pin, int flags)
|
|
{
|
|
struct awin_gpio_pin_group * const grp = cookie;
|
|
struct awin_gpio_pin_cfg ncfg = grp->grp_cfg;
|
|
|
|
u_int pull_value = AWIN_PIO_PULL_NONE;
|
|
if (flags & GPIO_PIN_PULLUP) {
|
|
pull_value = AWIN_PIO_PULL_UP;
|
|
} else if (flags & GPIO_PIN_PULLDOWN) {
|
|
pull_value = AWIN_PIO_PULL_DOWN;
|
|
}
|
|
awin_gpio_set_pin_pull(&ncfg, pin, pull_value);
|
|
|
|
if (flags & GPIO_PIN_INPUT) {
|
|
awin_gpio_set_pin_func(&ncfg, pin, AWIN_PIO_FUNC_INPUT);
|
|
} else if (flags & GPIO_PIN_OUTPUT) {
|
|
awin_gpio_set_pin_func(&ncfg, pin, AWIN_PIO_FUNC_OUTPUT);
|
|
}
|
|
|
|
/*
|
|
* Now update any config register that changed.
|
|
*/
|
|
awin_gpio_update_cfg_regs(&armv7_generic_bs_tag, grp, &ncfg);
|
|
}
|
|
|
|
bool
|
|
awin_gpio_pin_reserve(const char *name, struct awin_gpio_pindata *pd)
|
|
{
|
|
struct awin_gpio_softc * const sc = &awin_gpio_sc;
|
|
prop_dictionary_t dict = device_properties(sc->sc_dev);
|
|
const char *pin_data;
|
|
|
|
if (!prop_dictionary_get_cstring_nocopy(dict, name, &pin_data))
|
|
return false;
|
|
|
|
KASSERT(pin_data[0] == '>' || pin_data[0] == '<');
|
|
KASSERT(pin_data[1] == 'P');
|
|
|
|
#ifdef DIAGNOSTIC
|
|
if (awin_chip_id() == AWIN_CHIP_ID_A31) {
|
|
KASSERT(('A' <= pin_data[2] && pin_data[2] <= 'I') ||
|
|
('L' <= pin_data[2] && pin_data[2] <= 'M'));
|
|
} else if (awin_chip_id() == AWIN_CHIP_ID_A80) {
|
|
KASSERT(('A' <= pin_data[2] && pin_data[2] <= 'I') ||
|
|
('L' <= pin_data[2] && pin_data[2] <= 'N'));
|
|
} else {
|
|
KASSERT('A' <= pin_data[2] && pin_data[2] <= 'I');
|
|
}
|
|
#endif
|
|
struct awin_gpio_pin_group * const grp = &pin_groups[pin_data[2] - 'A'];
|
|
|
|
u_int pin = pin_data[3] - '0';
|
|
KASSERT(pin < 10);
|
|
if (pin_data[4] != 0) {
|
|
KASSERT(pin_data[5] == 0);
|
|
pin = pin * 10 + pin_data[4] - '0';
|
|
}
|
|
|
|
KASSERT(pin < 32);
|
|
KASSERT(grp->grp_offset != 0);
|
|
KASSERT(grp->grp_pin_mask & __BIT(pin));
|
|
KASSERT((grp->grp_pin_inuse_mask & __BIT(pin)) == 0);
|
|
|
|
struct awin_gpio_pin_cfg ncfg = grp->grp_cfg;
|
|
awin_gpio_set_pin_func(&ncfg, pin,
|
|
pin_data[0] == '<' ? AWIN_PIO_FUNC_INPUT : AWIN_PIO_FUNC_OUTPUT);
|
|
|
|
/*
|
|
* Now update any config register that changed.
|
|
*/
|
|
awin_gpio_update_cfg_regs(sc->sc_bst, grp, &ncfg);
|
|
|
|
grp->grp_pin_inuse_mask &= ~__BIT(pin);
|
|
|
|
pd->pd_gc = &grp->grp_gc_tag;
|
|
pd->pd_pin = pin;
|
|
return true;
|
|
}
|