Implement gpio framework for the Samsung Exynos series.

Note that only the Exynos4 gpio registers are defined now and provision is
taken to allow for seamlessness adding of Exynos5 values.
This commit is contained in:
reinoud 2014-05-09 21:49:43 +00:00
parent aaace33d0e
commit 5ad8bd1845
7 changed files with 934 additions and 11 deletions

View File

@ -264,6 +264,7 @@
EXYNOS4##p##_##n##_OFFSET, 0x10000
static const struct exyo_locators exynos4_locators[] = {
{ "exyogpio", 0, 0, NOPORT, NOINTR, 0 },
{ "mct", OFFANDSIZE(,MCT), NOPORT, IRQ_G0_IRQ, 0 },
{ "exyowdt", OFFANDSIZE(,WDT), NOPORT, IRQ_WDT, 0 },
{ "sscom", OFFANDSIZE(,UART0), 0, IRQ_UART0, 0 },

View File

@ -0,0 +1,791 @@
/*-
* Copyright (c) 2014 The NetBSD Foundation, Inc.
* All rights reserved.
*
* This code is derived from software contributed to The NetBSD Foundation
* by Reinoud Zandijk
*
* 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 "opt_exynos.h"
#include "opt_arm_debug.h"
#include "gpio.h"
#include <sys/cdefs.h>
__KERNEL_RCSID(1, "$NetBSD: exynos_gpio.c,v 1.1 2014/05/09 21:49:43 reinoud 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 <arm/samsung/exynos_reg.h>
#include <arm/samsung/exynos_io.h>
#include <arm/samsung/exynos_intr.h>
#include <sys/gpio.h>
#include <dev/gpio/gpiovar.h>
static int exynos_gpio_match(device_t, cfdata_t, void *);
static void exynos_gpio_attach(device_t, device_t, void *);
static int exynos_gpio_pin_read(void *, int);
static void exynos_gpio_pin_write(void *, int, int);
static void exynos_gpio_pin_ctl(void *, int, int);
struct exynos_gpio_pin_cfg {
uint32_t cfg;
uint32_t pud;
uint32_t drv;
uint32_t conpwd;
uint32_t pudpwd;
};
struct exynos_gpio_pin_group {
const char grp_name[6];
const bus_addr_t grp_core_offset;
const uint8_t grp_bits;
uint8_t grp_pin_mask;
uint8_t grp_pin_inuse_mask;
bus_space_handle_t grp_bsh;
struct exynos_gpio_pin_cfg grp_cfg;
struct gpio_chipset_tag grp_gc_tag;
};
#define GPIO_OFFSET(v,s,o) (EXYNOS##v##_GPIO_##s##_OFFSET + (o))
#define GPIO_GRP(v, s, o, n, b) \
{ \
.grp_name = #n, \
.grp_core_offset = GPIO_OFFSET(v,s,o), \
.grp_bits = b,\
}
#ifdef EXYNOS4
/*
* Exynos 4412 contains 304 multi-functional input/output port pins and 164
* memory port pins. There are 37 general port groups and two memory port
* groups. They are:
*
* GPA0, GPA1: 14 in/out ports-3xUART with flow control, UART without flow
* control, and/or 2xI2C
*
* GPB: 8 in/out ports-2xSPI and/or 2xI2C and/ or IEM
*
* GPC0, GPC1: 10 in/out ports-2xI2S, and/or 2xPCM, and/or AC97, SPDIF, I2C,
* and/or SPI
*
* GPD0, GPD1: 8 in/out ports-PWM, 2xI2C, and/ or LCD I/F, MIPI
*
* GPM0, GPM1, GPM2, GPM3, GPM4: 35 in/out ports-CAM I/F, and/ or TS I/F,
* HSI, and/ or Trace I/F
*
* GPF0, GPF1, GPF2, GPF3: 30 in/out ports-LCD I/F
*
* GPJ0, GPJ1: 13 in/out ports-CAM I/F
*
* GPK0, GPK1, GPK2, GPK3: 28 in/out ports-4xMMC (4-bit MMC), and/or 2xMMC
* (8-bit MMC)), and/or GPS debugging I/F
*
* GPL0, GPL1: 11 in/out ports-GPS I/F
*
* GPL2: 8 in/out ports-GPS debugging I/F or Key pad I/F
*
* GPX0, GPX1, GPX2, GPX3: 32 in/out ports-External wake-up, and/or Key pad
* I/F
*
* GPZ: 7 in/out ports-low Power I2S and/or PCM
*
* GPY0, GPY1, GPY2: 16 in/out ports-Control signals of EBI (SROM, NF, One
* NAND)
*
* GPY3, GPY4, GPY5, GPY6: 32 in/out memory ports-EBI (For more information
* about EBI configuration, refer to Chapter 5, and 6)
*
* MP1_0-MP1_9: 78 DRAM1 ports. NOTE: GPIO registers does not control these
* ports.
*
* MP2_0-MP2_9: 78 DRAM2 ports. NOTE: GPIO registers does not control these
* ports.
*
* ETC0, ETC1, ETC6: 18 in/out ETC ports-JTAG, SLIMBUS, RESET, CLOCK
*
* ETC7, ETC8 : 4 clock port for C2C
*
*/
static struct exynos_gpio_pin_group exynos4_pin_groups[] = {
GPIO_GRP(4, LEFT, 0x0000, GPA0, 8),
GPIO_GRP(4, LEFT, 0x0020, GPA1, 6),
GPIO_GRP(4, LEFT, 0x0040, GPB, 8),
GPIO_GRP(4, LEFT, 0x0060, GPC0, 5),
GPIO_GRP(4, LEFT, 0x0080, GPC1, 5),
GPIO_GRP(4, LEFT, 0x00A0, GPD0, 4),
GPIO_GRP(4, LEFT, 0x00C0, GPD1, 4),
GPIO_GRP(4, LEFT, 0x0180, GPF0, 8),
GPIO_GRP(4, LEFT, 0x01A0, GPF1, 8),
GPIO_GRP(4, LEFT, 0x01C0, GPF2, 8),
GPIO_GRP(4, LEFT, 0x01E0, GPF3, 8),
GPIO_GRP(4, LEFT, 0x0240, GPJ0, 8),
GPIO_GRP(4, LEFT, 0x0260, GPJ1, 5),
/* EXTINT skipped */
GPIO_GRP(4, RIGHT, 0x0040, GPK0, 8),
GPIO_GRP(4, RIGHT, 0x0060, GPK1, 8),
GPIO_GRP(4, RIGHT, 0x0080, GPK2, 7),
GPIO_GRP(4, RIGHT, 0x00A0, GPK3, 7),
GPIO_GRP(4, RIGHT, 0x00C0, GPL0, 7),
GPIO_GRP(4, RIGHT, 0x00E0, GPL1, 2),
GPIO_GRP(4, RIGHT, 0x0100, GPL2, 8),
GPIO_GRP(4, RIGHT, 0x0120, GPY0, 6),
GPIO_GRP(4, RIGHT, 0x0140, GPY1, 4),
GPIO_GRP(4, RIGHT, 0x0160, GPY2, 6),
GPIO_GRP(4, RIGHT, 0x0180, GPY3, 8),
GPIO_GRP(4, RIGHT, 0x01A0, GPY4, 8),
GPIO_GRP(4, RIGHT, 0x01C0, GPY5, 8),
GPIO_GRP(4, RIGHT, 0x01E0, GPY6, 8),
/* ETC0, ETC6 skipped */
GPIO_GRP(4, RIGHT, 0x0260, GPM0, 8),
GPIO_GRP(4, RIGHT, 0x0280, GPM1, 7),
GPIO_GRP(4, RIGHT, 0x02A0, GPM2, 5),
GPIO_GRP(4, RIGHT, 0x02C0, GPM3, 8),
GPIO_GRP(4, RIGHT, 0x02E0, GPM4, 8),
/* EXTINT skipped */
GPIO_GRP(4, RIGHT, 0x0C00, GPX0, 8),
GPIO_GRP(4, RIGHT, 0x0C20, GPX1, 8),
GPIO_GRP(4, RIGHT, 0x0C40, GPX2, 8),
GPIO_GRP(4, RIGHT, 0x0C60, GPX3, 8),
/* EXTINT skipped */
GPIO_GRP(4, I2C0, 0x0000, GPZ, 8),
/* EXTINT skipped */
GPIO_GRP(4, C2C, 0x0000, GPV0, 8),
GPIO_GRP(4, C2C, 0x0020, GPV1, 8),
/* ETC7 skipped */
GPIO_GRP(4, C2C, 0x0060, GPV2, 8),
GPIO_GRP(4, C2C, 0x0080, GPV3, 8),
/* ETC8 skipped */
GPIO_GRP(4, C2C, 0x00C0, GPV4, 2),
/* EXTINT skipped */
};
#endif
#ifdef EXYNOS5
static struct exynos_gpio_pin_group exynos5_pin_groups[] = {
};
#endif
struct exynos_gpio_softc {
device_t sc_dev;
bus_space_tag_t sc_bst;
bus_space_handle_t sc_bsh;
};
/* force these structures in DATA segment */
static struct exynos_gpio_pin_group *exynos_pin_groups = NULL;
static int exynos_n_pin_groups = 0;
static struct exynos_gpio_softc exynos_gpio_sc = {};
CFATTACH_DECL_NEW(exynos_gpio, sizeof(struct exynos_gpio_softc),
exynos_gpio_match, exynos_gpio_attach, NULL, NULL);
static int
exynos_gpio_match(device_t parent, cfdata_t cf, void *aux)
{
struct exyo_attach_args * const exyoaa = aux;
struct exyo_locators *loc = &exyoaa->exyo_loc;
/* no locators expected */
KASSERT(loc->loc_offset == 0);
KASSERT(loc->loc_size == 0);
KASSERT(loc->loc_port == EXYOCF_PORT_DEFAULT);
/* there can only be one */
if (exynos_gpio_sc.sc_dev != NULL)
return 0;
return 1;
}
#if NGPIO > 0
static void
exynos_gpio_config_pins(device_t self)
{
struct exynos_gpio_softc * const sc = &exynos_gpio_sc;
struct exynos_gpio_pin_group *grp;
struct gpiobus_attach_args gba;
gpio_pin_t *pin, *pins;
size_t pin_count = 0;
int i, bit, mask, pincaps, data;
/* find out how many pins we can offer */
pin_count = 0;
for (i = 0; i < exynos_n_pin_groups; i++) {
grp = &exynos_pin_groups[i];
mask = grp->grp_pin_mask & ~grp->grp_pin_inuse_mask;
pin_count += popcount32(mask);
}
/* if no pins available, don't proceed */
if (pin_count == 0)
return;
/* allocate pin data */
pins = kmem_zalloc(sizeof(gpio_pin_t) * pin_count, KM_SLEEP);
KASSERT(pins);
pincaps = GPIO_PIN_INPUT | GPIO_PIN_OUTPUT |
GPIO_PIN_PULLUP | GPIO_PIN_PULLDOWN;
/* add all pins */
pin = pins;
for (i = 0; i < exynos_n_pin_groups; i++) {
grp = &exynos_pin_groups[i];
mask = grp->grp_pin_mask & ~grp->grp_pin_inuse_mask;
if (mask == 0)
continue;
gba.gba_gc = &grp->grp_gc_tag;
gba.gba_pins = pin;
data = bus_space_read_1(sc->sc_bst, grp->grp_bsh,
EXYNOS_GPIO_DAT);
for (bit = 0; mask != 0; mask >>= 1, data >>= 1, bit++) {
if (mask & 1) {
pin->pin_num = bit + (i << 3);
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, gpiobus_print);
}
}
#endif
static void
exynos_gpio_attach(device_t parent, device_t self, void *aux)
{
struct exynos_gpio_softc * const sc = &exynos_gpio_sc;
struct exyo_attach_args * const exyoaa = aux;
struct exynos_gpio_pin_group *grp;
prop_dictionary_t dict = device_properties(self);
uint32_t nc;
char scrap[16];
int i;
KASSERT(exynos_pin_groups);
KASSERT(exynos_n_pin_groups);
/* construct softc */
sc->sc_dev = self;
/* we use the core bushandle here */
sc->sc_bst = exyoaa->exyo_core_bst;
sc->sc_bsh = exyoaa->exyo_core_bsh;
aprint_naive("\n");
aprint_normal("\n");
/* go trough all pin groups */
for (i = 0; i < exynos_n_pin_groups; i++) {
grp = &exynos_pin_groups[i];
snprintf(scrap, sizeof(scrap), "nc-%s", grp->grp_name);
if (prop_dictionary_get_uint32(dict, scrap, &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, exynos_gpio_config_pins);
#endif
}
/* pin access functions */
static u_int
exynos_gpio_get_pin_func(const struct exynos_gpio_pin_cfg *cfg, int pin)
{
const u_int shift = (pin & 7) << 4;
return (cfg->cfg >> shift) & 0x0f;
}
static void
exynos_gpio_set_pin_func(struct exynos_gpio_pin_cfg *cfg,
int pin, int func)
{
const u_int shift = (pin & 7) << 4;
cfg->cfg &= ~(0x0f << shift);
cfg->cfg |= func << shift;
}
static void
exynos_gpio_set_pin_pull(struct exynos_gpio_pin_cfg *cfg, int pin, int pull)
{
const u_int shift = (pin & 7) << 1;
cfg->pud &= ~(0x3 << shift);
cfg->pud |= pull << shift;
}
static int
exynos_gpio_pin_read(void *cookie, int pin)
{
struct exynos_gpio_pin_group * const grp = cookie;
KASSERT(pin < grp->grp_bits);
return (bus_space_read_1(exynos_gpio_sc.sc_bst, grp->grp_bsh,
EXYNOS_GPIO_DAT) >> pin) & 1;
}
static void
exynos_gpio_pin_write(void *cookie, int pin, int value)
{
struct exynos_gpio_pin_group * const grp = cookie;
int val;
KASSERT(pin < grp->grp_bits);
val = bus_space_read_1(exynos_gpio_sc.sc_bst, grp->grp_bsh,
EXYNOS_GPIO_DAT);
val &= ~__BIT(pin);
if (value)
val |= __BIT(pin);
bus_space_write_1(exynos_gpio_sc.sc_bst, grp->grp_bsh,
EXYNOS_GPIO_DAT, val);
}
static void
exynos_gpio_update_cfg_regs(struct exynos_gpio_pin_group *grp,
const struct exynos_gpio_pin_cfg *ncfg)
{
bus_space_tag_t bst = &exynos_bs_tag;
if (grp->grp_cfg.cfg != ncfg->cfg) {
bus_space_write_4(bst, grp->grp_bsh,
EXYNOS_GPIO_CON, ncfg->cfg);
grp->grp_cfg.cfg = ncfg->cfg;
}
if (grp->grp_cfg.pud != ncfg->pud) {
bus_space_write_4(bst, grp->grp_bsh,
EXYNOS_GPIO_PUD, ncfg->pud);
grp->grp_cfg.pud = ncfg->pud;
}
/* the following attributes are not yet setable */
#if 0
if (grp->grp_cfg.drv != ncfg->drv) {
bus_space_write_4(bst, grp->grp_bsh,
EXYNOS_GPIO_DRV, ncfg->drv);
grp->grp_cfg.drv = ncfg->drv;
}
if (grp->grp_cfg.conpwd != ncfg->conpwd) {
bus_space_write_4(bst, grp->grp_bsh,
EXYNOS_GPIO_CONPWD, ncfg->conpwd);
grp->grp_cfg.conpwd = ncfg->conpwd;
}
if (grp->grp_cfg.pudpwd != ncfg->pudpwd) {
bus_space_write_4(bst, grp->grp_bsh,
EXYNOS_GPIO_PUDPWD, ncfg->pudpwd);
grp->grp_cfg.pudpwd = ncfg->pudpwd;
}
#endif
}
static void
exynos_gpio_pin_ctl(void *cookie, int pin, int flags)
{
struct exynos_gpio_pin_group * const grp = cookie;
struct exynos_gpio_pin_cfg ncfg = grp->grp_cfg;
int pull;
/* honour pullup requests */
pull = EXYNOS_GPIO_PIN_FLOAT;
if (flags & GPIO_PIN_PULLUP)
pull = EXYNOS_GPIO_PIN_PULL_UP;
if (flags & GPIO_PIN_PULLDOWN)
pull = EXYNOS_GPIO_PIN_PULL_DOWN;
exynos_gpio_set_pin_pull(&ncfg, pin, pull);
/* honour i/o */
if (flags & GPIO_PIN_INPUT)
exynos_gpio_set_pin_func(&ncfg, pin, EXYNOS_GPIO_FUNC_INPUT);
if (flags & GPIO_PIN_OUTPUT)
exynos_gpio_set_pin_func(&ncfg, pin, EXYNOS_GPIO_FUNC_OUTPUT);
/* update any config registers that changed */
exynos_gpio_update_cfg_regs(grp, &ncfg);
}
bool
exynos_gpio_pinset_available(const struct exynos_gpio_pinset *req)
{
struct exynos_gpio_pin_group *grp;
int i, n, inuse;
KASSERT(req);
/* we need a pinset group */
if (strlen(req->pinset_group) == 0)
return false;
/* determine which group is requested */
grp = NULL;
for (i = 0; i < exynos_n_pin_groups; i++) {
grp = &exynos_pin_groups[i];
if (strcmp(req->pinset_group, grp->grp_name) == 0)
break;
}
/* found? */
if (i == exynos_n_pin_groups)
return false;
KASSERT(grp);
/* fail unconnected pins */
if (req->pinset_mask & ~grp->grp_pin_mask)
return false;
/* if none in use, they are available */
if (req->pinset_mask & ~grp->grp_pin_inuse_mask)
return true;
/* OK, so some are in use; now see if the request is compatible */
inuse = req->pinset_mask & grp->grp_pin_inuse_mask;
for (i = 0; inuse; i++, inuse >>= 1) {
/* try to be smart by skipping zero's */
n = ffs(inuse) -1;
i += n;
inuse >>= n;
/* this pin is in use, check its usage */
if (exynos_gpio_get_pin_func(&grp->grp_cfg, i) != req->pinset_func)
return false;
}
/* seems to be OK */
return true;
}
void
exynos_gpio_pinset_acquire(const struct exynos_gpio_pinset *req)
{
struct exynos_gpio_pin_group *grp;
struct exynos_gpio_pin_cfg ncfg;
int i, n, todo;
KASSERT(req);
KASSERT(exynos_gpio_pinset_available(req));
/* determine which group is requested */
grp = NULL;
for (i = 0; i < exynos_n_pin_groups; i++) {
grp = &exynos_pin_groups[i];
if (strcmp(req->pinset_group, grp->grp_name) == 0)
break;
}
KASSERT(grp);
/* check if all the pins have the right function */
if ((req->pinset_mask & ~grp->grp_pin_inuse_mask) == 0)
return;
/* copy current config for update routine */
ncfg = grp->grp_cfg;
/* update the function of each pin that is not in use */
todo = req->pinset_mask & grp->grp_pin_inuse_mask;
for (i = 0; todo; i++, todo >>= 1) {
/* try to be smart by skipping zero's */
n = ffs(todo) -1;
i += n;
todo >>= n;
/* change the function of this pin */
exynos_gpio_set_pin_func(&ncfg, i, req->pinset_func);
}
/* update config registers */
exynos_gpio_update_cfg_regs(grp, &ncfg);
/* mark pins in use */
grp->grp_pin_inuse_mask |= req->pinset_mask;
}
/* XXXRPZ This release doesn't grock multiple usages! */
void
exynos_gpio_pinset_release(const struct exynos_gpio_pinset *req)
{
struct exynos_gpio_pin_group *grp;
int i;
KASSERT(!exynos_gpio_pinset_available(req));
/* determine which group is requested */
grp = NULL;
for (i = 0; i < exynos_n_pin_groups; i++) {
grp = &exynos_pin_groups[i];
if (strcmp(req->pinset_group, grp->grp_name) == 0)
break;
}
KASSERT(grp);
/* bluntly mark as not being in use */
grp->grp_pin_inuse_mask &= ~req->pinset_mask;
}
/*
* name convention :
* pin = <func><groupname><pinnr>[<pud>]
* func = '<' | '>'
* pinnr = '['['0'-'7']']'
* pud = 'F' | 'U' | 'D'
*
* example "<GPC1[0]", ">GPB[0]"
*/
bool
exynos_gpio_pin_reserve(const char *name, struct exynos_gpio_pindata *pd)
{
struct exynos_gpio_softc * const sc = &exynos_gpio_sc;
struct exynos_gpio_pin_group *grp;
struct exynos_gpio_pin_cfg ncfg;
prop_dictionary_t dict = device_properties(sc->sc_dev);
const char *pin_data;
char grp_name[15], *pos;
int func, pud, pinnr;
int pi, i;
/* do we have a named pin description? */
if (!prop_dictionary_get_cstring_nocopy(dict, name, &pin_data))
return false;
KASSERT(strlen(pin_data) < 10);
if (!(pin_data[0] == '>' || pin_data[1] == '<')) {
printf("%s: malformed pin data in '%s', missing direction\n",
__func__, pin_data);
return false;
}
func = (pin_data[0] == '<') ?
EXYNOS_GPIO_FUNC_INPUT : EXYNOS_GPIO_FUNC_OUTPUT;
/* find groupname */
pi = 1; pos = grp_name;
while (pin_data[pi] && pin_data[pi] != '[') {
*pos++ = pin_data[pi++];
}
if (pin_data[pi] != '[') {
printf("%s: malformed pin data in '%s', missing '['\n",
__func__, pin_data);
return false;
}
*pos++ = (char) 0;
/* skip '[' */
pi++;
if (!(pin_data[pi] >= '0' && pin_data[pi] <= '7')) {
printf("%s: malformed pin data in '%s', bad pin number\n",
__func__, pin_data);
return false;
}
pinnr = pin_data[pi] - '0';
/* skip digit */
pi++;
if ((pin_data[pi] != ']')) {
printf("%s: malformed pin data in '%s', missing end ']'\n",
__func__, pin_data);
return false;
}
/* skip ']' */
pi++;
pud = EXYNOS_GPIO_PIN_FLOAT;
switch (tolower(pin_data[pi])) {
case (char) 0:
break;
case 'f':
pud = EXYNOS_GPIO_PIN_FLOAT;
break;
case 'u':
pud = EXYNOS_GPIO_PIN_PULL_UP;
break;
case 'd':
pud = EXYNOS_GPIO_PIN_PULL_DOWN;
break;
default:
printf("%s: malformed pin data in '%s', expecting "
"optional pull up/down or float argument\n",
__func__, pin_data);
return false;
}
/* determine which group is requested */
grp = NULL;
for (i = 0; i < exynos_n_pin_groups; i++) {
grp = &exynos_pin_groups[i];
if (strcmp(grp_name, grp->grp_name) == 0)
break;
}
/* found? */
if (i >= exynos_n_pin_groups) {
printf("%s: malformed pin data in '%s', "
"no such pin group name\n",
__func__, grp_name);
return false;
}
KASSERT(grp);
KASSERT(pinnr < grp->grp_bits);
KASSERT(grp->grp_pin_mask & __BIT(pinnr));
KASSERT((grp->grp_pin_inuse_mask & __BIT(pinnr)) == 0);
/* update our pin configuration */
ncfg = grp->grp_cfg;
exynos_gpio_set_pin_func(&ncfg, pinnr, func);
exynos_gpio_set_pin_pull(&ncfg, pinnr, pud);
exynos_gpio_update_cfg_regs(grp, &ncfg);
grp->grp_pin_inuse_mask &= ~__BIT(pinnr);
pd->pd_gc = &grp->grp_gc_tag;
pd->pd_pin = pinnr;
return true;
}
/* bootstrapping */
void
exynos_gpio_bootstrap(void)
{
bus_space_tag_t bst = &exynos_bs_tag;
struct exynos_gpio_pin_group *grp;
struct gpio_chipset_tag *gc_tag;
int i, j, func, mask;
/* determine what we're running on */
#ifdef EXYNOS4
if (IS_EXYNOS4_P()) {
exynos_pin_groups = exynos4_pin_groups;
exynos_n_pin_groups = __arraycount(exynos4_pin_groups);
}
#endif
#ifdef EXYNOS5
if (IS_EXYNOS5_P()) {
exynos_pin_groups = exynos5_pin_groups;
exynos_n_pin_groups = __arraycount(exynos5_pin_groups);
}
#endif
#ifdef VERBOSE_INIT_ARM
printf("gpio");
#endif
if (exynos_n_pin_groups == 0) {
#ifdef VERBOSE_INIT_ARM
printf(" (disabled)\n");
#endif
return;
}
#ifdef VERBOSE_INIT_ARM
printf(" free");
#endif
/* init groups */
for (i = 0; i < exynos_n_pin_groups; i++) {
grp = &exynos_pin_groups[i];
gc_tag = &grp->grp_gc_tag;
bus_space_subregion(&exynos_bs_tag, exynos_core_bsh,
grp->grp_core_offset, EXYNOS_GPIO_GRP_SIZE,
&grp->grp_bsh);
grp->grp_pin_mask = __BIT(grp->grp_bits) - 1;
grp->grp_pin_inuse_mask = 0;
gc_tag->gp_cookie = grp;
gc_tag->gp_pin_read = exynos_gpio_pin_read;
gc_tag->gp_pin_write = exynos_gpio_pin_write;
gc_tag->gp_pin_ctl = exynos_gpio_pin_ctl;
/* read in our initial settings */
grp->grp_cfg.cfg = bus_space_read_4(bst, grp->grp_bsh,
EXYNOS_GPIO_CON);
grp->grp_cfg.pud = bus_space_read_4(bst, grp->grp_bsh,
EXYNOS_GPIO_PUD);
grp->grp_cfg.drv = bus_space_read_4(bst, grp->grp_bsh,
EXYNOS_GPIO_DRV);
grp->grp_cfg.conpwd = bus_space_read_4(bst, grp->grp_bsh,
EXYNOS_GPIO_CONPWD);
grp->grp_cfg.pudpwd = bus_space_read_4(bst, grp->grp_bsh,
EXYNOS_GPIO_PUDPWD);
/* count number of busy pins */
for (j = 0, mask = 1;
(mask & grp->grp_pin_mask) != 0;
j++, mask <<= 1) {
func = exynos_gpio_get_pin_func(&grp->grp_cfg, j);
if (func > EXYNOS_GPIO_FUNC_INPUT) {
grp->grp_pin_inuse_mask |= mask;
}
}
#ifdef VERBOSE_INIT_ARM
printf(" P%s = %d", grp->grp_name,
popcount32(grp->grp_pin_mask & ~grp->grp_pin_inuse_mask));
#endif
}
#ifdef VERBOSE_INIT_ARM
printf("\n");
#if 0
/* enable this for default NC pins list generation */
for (i = 0; i < exynos_n_pin_groups; i++) {
grp = &exynos_pin_groups[i];
printf("prop_dictionary_set_uint32(dict, \"nc-%s\", 0x%02x - 0x00);\n",
grp->grp_name, grp->grp_pin_mask);
}
#endif
#endif
}

View File

@ -1,4 +1,4 @@
/* $NetBSD: exynos_soc.c,v 1.7 2014/04/29 16:47:10 reinoud Exp $ */
/* $NetBSD: exynos_soc.c,v 1.8 2014/05/09 21:49:43 reinoud Exp $ */
/*-
* Copyright (c) 2014 The NetBSD Foundation, Inc.
* All rights reserved.
@ -33,7 +33,7 @@
#define _ARM32_BUS_DMA_PRIVATE
#include <sys/cdefs.h>
__KERNEL_RCSID(1, "$NetBSD: exynos_soc.c,v 1.7 2014/04/29 16:47:10 reinoud Exp $");
__KERNEL_RCSID(1, "$NetBSD: exynos_soc.c,v 1.8 2014/05/09 21:49:43 reinoud Exp $");
#include <sys/param.h>
#include <sys/bus.h>
@ -234,6 +234,9 @@ exynos_bootstrap(vaddr_t iobase, vaddr_t uartbase)
/* init bus dma tags */
exynos_dma_bootstrap(physmem * PAGE_SIZE);
/* init gpio structures */
exynos_gpio_bootstrap();
}

View File

@ -1,4 +1,4 @@
/* $NetBSD: exynos_var.h,v 1.4 2014/04/29 16:47:10 reinoud Exp $ */
/* $NetBSD: exynos_var.h,v 1.5 2014/05/09 21:49:43 reinoud Exp $ */
/*-
* Copyright (c) 2013, 2014 The NetBSD Foundation, Inc.
* All rights reserved.
@ -37,10 +37,9 @@
#include <sys/types.h>
#include <sys/bus.h>
#include <sys/gpio.h>
#include <dev/gpio/gpiovar.h>
#include <arm/samsung/exynos_reg.h>
//#include <dev/gpio/gpiovar.h>
extern uint32_t exynos_soc_id;
extern uint32_t exynos_pop_id;
@ -95,6 +94,17 @@ struct exyo_attach_args {
bus_dma_tag_t exyo_coherent_dmat;
};
struct exynos_gpio_pinset {
char pinset_group[10];
uint8_t pinset_func;
uint32_t pinset_mask;
};
struct exynos_gpio_pindata {
gpio_chipset_tag_t pd_gc;
int pd_pin;
};
extern struct bus_space exynos_bs_tag;
extern struct bus_space exynos_a4x_bs_tag;
@ -104,10 +114,29 @@ extern struct arm32_bus_dma_tag exynos_coherent_bus_dma_tag;
extern bus_space_handle_t exynos_core_bsh;
extern void exynos_bootstrap(vaddr_t, vaddr_t);
extern void exynos_dma_bootstrap(psize_t memsize);
extern void exynos_gpio_bootstrap(void);
extern void exynos_device_register(device_t self, void *aux);
extern void exyo_device_register(device_t self, void *aux);
extern void exynos_wdt_reset(void);
extern void exynos_dma_bootstrap(psize_t memsize);
extern bool exynos_gpio_pinset_available(const struct exynos_gpio_pinset *);
extern void exynos_gpio_pinset_acquire(const struct exynos_gpio_pinset *);
extern void exynos_gpio_pinset_release(const struct exynos_gpio_pinset *);
extern bool exynos_gpio_pin_reserve(const char *, struct exynos_gpio_pindata *);
static inline void
exynos_gpio_pindata_write(const struct exynos_gpio_pindata *pd, int value)
{
gpiobus_pin_write(pd->pd_gc, pd->pd_pin, value);
}
static inline int
exynos_gpio_pindata_read(const struct exynos_gpio_pindata *pd)
{
return gpiobus_pin_read(pd->pd_gc, pd->pd_pin);
}
#ifdef ARM_TRUSTZONE_FIRMWARE

View File

@ -1,4 +1,4 @@
# $NetBSD: files.exynos,v 1.5 2014/05/05 20:31:03 reinoud Exp $
# $NetBSD: files.exynos,v 1.6 2014/05/09 21:49:43 reinoud Exp $
#
# Configuration info for Samsung Exynos SoC ARM Peripherals
#
@ -45,7 +45,7 @@ defflag opt_exynos.h EXYNOS5440: EXYNOS5
defflag opt_exynos.h EXYNOS5422: EXYNOS5
# SoC I/O attach point
device exyo { [port=-1] } : bus_space_generic
device exyo { [port=-1], [intr=-1] } : bus_space_generic
attach exyo at mainbus with exyo_io
file arch/arm/samsung/exynos_io.c exyo_io
file arch/arm/samsung/exynos4_loc.c exyo_io & exynos4
@ -69,6 +69,11 @@ file arch/arm/samsung/exynos_sscom.c exynos_sscom
defflag opt_sscom.h SSCOM0CONSOLE SSCOM1CONSOLE
defparam opt_sscom.h SSCOM_FREQ
# GPIO
device exyogpio : gpiobus
attach exyogpio at exyo with exynos_gpio
file arch/arm/samsung/exynos_gpio.c exynos_gpio | exyo_io needs-flag
# USB2 Host Controller (EHCI/OHCI)
device exyousb { }
attach exyousb at exyo with exyo_usb

View File

@ -1,5 +1,5 @@
#
# $NetBSD: ODROID-U,v 1.4 2014/05/05 20:31:03 reinoud Exp $
# $NetBSD: ODROID-U,v 1.5 2014/05/09 21:49:43 reinoud Exp $
#
# ODROID-U -- ODROID-U series Exynos Kernel
#
@ -192,6 +192,10 @@ sscom1 at exyo0 port 1 # UART1
# Exynos Watchdog Timer
exyowdt0 at exyo0 flags 1 # watchdog
# GPIO
exyogpio0 at exyo0
gpio* at exyogpio?
# On-board USB
exyousb* at exyo0
ohci* at exyousb?

View File

@ -1,4 +1,4 @@
/* $NetBSD: odroid_machdep.c,v 1.8 2014/04/19 19:47:55 matt Exp $ */
/* $NetBSD: odroid_machdep.c,v 1.9 2014/05/09 21:49:43 reinoud Exp $ */
/*
* Copyright (c) 2014 The NetBSD Foundation, Inc.
@ -31,7 +31,7 @@
*/
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: odroid_machdep.c,v 1.8 2014/04/19 19:47:55 matt Exp $");
__KERNEL_RCSID(0, "$NetBSD: odroid_machdep.c,v 1.9 2014/05/09 21:49:43 reinoud Exp $");
#include "opt_evbarm_boardtype.h"
#include "opt_exynos.h"
@ -469,6 +469,75 @@ consinit(void)
#endif
}
#ifdef EXYNOS4
static void
odroid_exynos4_gpio_ncs(device_t self, prop_dictionary_t dict) {
/*
* These pins have no connections; the 1st is a raw values that is
* generated by the gpio bootstrap and the values substracted are
* explicitly allowed
*/
prop_dictionary_set_uint32(dict, "nc-GPA0", 0xff - 0x00);
prop_dictionary_set_uint32(dict, "nc-GPA1", 0x3f - 0x00);
prop_dictionary_set_uint32(dict, "nc-GPB", 0xff - 0x00);
prop_dictionary_set_uint32(dict, "nc-GPC0", 0x1f - 0x00);
/* blue led at bit 0 : */
prop_dictionary_set_uint32(dict, "nc-GPC1", 0x1f - 0x01);
prop_dictionary_set_uint32(dict, "nc-GPD0", 0x0f - 0x00);
prop_dictionary_set_uint32(dict, "nc-GPD1", 0x0f - 0x00);
prop_dictionary_set_uint32(dict, "nc-GPF0", 0xff - 0x00);
prop_dictionary_set_uint32(dict, "nc-GPF1", 0xff - 0x00);
prop_dictionary_set_uint32(dict, "nc-GPF2", 0xff - 0x00);
prop_dictionary_set_uint32(dict, "nc-GPF3", 0xff - 0x00);
prop_dictionary_set_uint32(dict, "nc-GPJ0", 0xff - 0x00);
prop_dictionary_set_uint32(dict, "nc-GPJ1", 0x1f - 0x00);
prop_dictionary_set_uint32(dict, "nc-GPK0", 0xff - 0x00);
prop_dictionary_set_uint32(dict, "nc-GPK1", 0xff - 0x00);
prop_dictionary_set_uint32(dict, "nc-GPK2", 0x7f - 0x00);
prop_dictionary_set_uint32(dict, "nc-GPK3", 0x7f - 0x00);
prop_dictionary_set_uint32(dict, "nc-GPL0", 0x7f - 0x00);
prop_dictionary_set_uint32(dict, "nc-GPL1", 0x03 - 0x00);
prop_dictionary_set_uint32(dict, "nc-GPL2", 0xff - 0x00);
prop_dictionary_set_uint32(dict, "nc-GPY0", 0x3f - 0x00);
prop_dictionary_set_uint32(dict, "nc-GPY1", 0x0f - 0x00);
prop_dictionary_set_uint32(dict, "nc-GPY2", 0x3f - 0x00);
prop_dictionary_set_uint32(dict, "nc-GPY3", 0xff - 0x00);
prop_dictionary_set_uint32(dict, "nc-GPY4", 0xff - 0x00);
prop_dictionary_set_uint32(dict, "nc-GPY5", 0xff - 0x00);
prop_dictionary_set_uint32(dict, "nc-GPY6", 0xff - 0x00);
prop_dictionary_set_uint32(dict, "nc-GPM0", 0xff - 0x00);
prop_dictionary_set_uint32(dict, "nc-GPM1", 0x7f - 0x00);
prop_dictionary_set_uint32(dict, "nc-GPM2", 0x1f - 0x00);
prop_dictionary_set_uint32(dict, "nc-GPM3", 0xff - 0x00);
prop_dictionary_set_uint32(dict, "nc-GPM4", 0xff - 0x00);
prop_dictionary_set_uint32(dict, "nc-GPX0", 0xff - 0x00);
prop_dictionary_set_uint32(dict, "nc-GPX1", 0xff - 0x00);
prop_dictionary_set_uint32(dict, "nc-GPX2", 0xff - 0x00);
prop_dictionary_set_uint32(dict, "nc-GPX3", 0xff - 0x00);
prop_dictionary_set_uint32(dict, "nc-GPZ", 0xff - 0x00);
prop_dictionary_set_uint32(dict, "nc-GPV0", 0xff - 0x00);
prop_dictionary_set_uint32(dict, "nc-GPV1", 0xff - 0x00);
prop_dictionary_set_uint32(dict, "nc-GPV2", 0xff - 0x00);
prop_dictionary_set_uint32(dict, "nc-GPV3", 0xff - 0x00);
prop_dictionary_set_uint32(dict, "nc-GPV4", 0x03 - 0x00);
}
#endif
#ifdef EXYNOS5
static void
odroid_exynos5_gpio_ncs(device_t self, prop_dictionary_t dict) {
/*
* These pins have no connections; the 1st is a raw values that is
* generated by the gpio bootstrap and the values substracted are
* explicitly allowed
*/
/* TBD: generate these values, see exynos_gpio.c boostrap */
}
#endif
void
odroid_device_register(device_t self, void *aux)
{
@ -480,5 +549,26 @@ odroid_device_register(device_t self, void *aux)
if (use_fb_console)
prop_dictionary_set_bool(dict, "is_console", false);
}
if (device_is_a(self, "mct"))
prop_dictionary_set_cstring(dict, "blinkled", "led0");
if (device_is_a(self, "exyogpio")) {
#ifdef EXYNOS4
if (IS_EXYNOS4_P()) {
/* unused bits */
odroid_exynos4_gpio_ncs(self, dict);
/* explicit pin settings */
prop_dictionary_set_cstring(dict, "led0", ">GPC1[0]");
}
#endif
#ifdef EXYNOS5
if (IS_EXYNOS5_P()) {
/* unused bits */
odroid_exynos5_gpio_ncs(self, dict);
/* explicit pin settings */
}
#endif
}
}