/*-
 * SPDX-License-Identifier: BSD-2-Clause
 *
 * Copyright 2020 Michal Meloun <mmel@FreeBSD.org>
 *
 * 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>
/*
 * Pin multiplexer driver for Tegra SoCs.
 */
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/bus.h>
#include <sys/kernel.h>
#include <sys/module.h>
#include <sys/malloc.h>
#include <sys/rman.h>

#include <machine/bus.h>

#include <dev/fdt/fdt_common.h>
#include <dev/fdt/fdt_pinctrl.h>
#include <dev/ofw/openfirm.h>
#include <dev/ofw/ofw_bus.h>
#include <dev/ofw/ofw_bus_subr.h>

/* Pin multipexor register. */
#define	TEGRA_MUX_FUNCTION_MASK  0x03
#define	TEGRA_MUX_FUNCTION_SHIFT 0
#define	TEGRA_MUX_PUPD_MASK  0x03
#define	TEGRA_MUX_PUPD_SHIFT 2
#define	TEGRA_MUX_TRISTATE_SHIFT 4
#define	TEGRA_MUX_ENABLE_INPUT_SHIFT 5
#define	TEGRA_MUX_OPEN_DRAIN_SHIFT 6
#define	TEGRA_MUX_LOCK_SHIFT 7
#define	TEGRA_MUX_IORESET_SHIFT 8
#define	TEGRA_MUX_RCV_SEL_SHIFT 9


/* Pin goup register. */
#define	TEGRA_GRP_HSM_SHIFT 2
#define	TEGRA_GRP_SCHMT_SHIFT 3
#define	TEGRA_GRP_DRV_TYPE_SHIFT 6
#define	TEGRA_GRP_DRV_TYPE_MASK 0x03
#define	TEGRA_GRP_DRV_DRVDN_SLWR_SHIFT 28
#define	TEGRA_GRP_DRV_DRVDN_SLWR_MASK 0x03
#define	TEGRA_GRP_DRV_DRVUP_SLWF_SHIFT 30
#define	TEGRA_GRP_DRV_DRVUP_SLWF_MASK 0x03

struct pinmux_softc {
	device_t	dev;
	struct resource	*pad_mem_res;
	struct resource	*mux_mem_res;
};

static struct ofw_compat_data compat_data[] = {
	{"nvidia,tegra210-pinmux",	1},
	{NULL,				0},
};

enum prop_id {
	PROP_ID_PULL,
	PROP_ID_TRISTATE,
	PROP_ID_ENABLE_INPUT,
	PROP_ID_OPEN_DRAIN,
	PROP_ID_LOCK,
	PROP_ID_IORESET,
	PROP_ID_RCV_SEL,
	PROP_ID_HIGH_SPEED_MODE,
	PROP_ID_SCHMITT,
	PROP_ID_LOW_POWER_MODE,
	PROP_ID_DRIVE_DOWN_STRENGTH,
	PROP_ID_DRIVE_UP_STRENGTH,
	PROP_ID_SLEW_RATE_FALLING,
	PROP_ID_SLEW_RATE_RISING,
	PROP_ID_DRIVE_TYPE,

	PROP_ID_MAX_ID
};

/* Numeric based parameters. */
static const struct prop_name {
	const char *name;
	enum prop_id id;
} prop_names[] = {
	{"nvidia,pull",			PROP_ID_PULL},
	{"nvidia,tristate",		PROP_ID_TRISTATE},
	{"nvidia,enable-input",		PROP_ID_ENABLE_INPUT},
	{"nvidia,open-drain",		PROP_ID_OPEN_DRAIN},
	{"nvidia,lock",			PROP_ID_LOCK},
	{"nvidia,io-reset",		PROP_ID_IORESET},
	{"nvidia,rcv-sel",		PROP_ID_RCV_SEL},
	{"nvidia,io-hv",		PROP_ID_RCV_SEL},
	{"nvidia,high-speed-mode",	PROP_ID_HIGH_SPEED_MODE},
	{"nvidia,schmitt",		PROP_ID_SCHMITT},
	{"nvidia,low-power-mode",	PROP_ID_LOW_POWER_MODE},
	{"nvidia,pull-down-strength",	PROP_ID_DRIVE_DOWN_STRENGTH},
	{"nvidia,pull-up-strength",	PROP_ID_DRIVE_UP_STRENGTH},
	{"nvidia,slew-rate-falling",	PROP_ID_SLEW_RATE_FALLING},
	{"nvidia,slew-rate-rising",	PROP_ID_SLEW_RATE_RISING},
	{"nvidia,drive-type",		PROP_ID_DRIVE_TYPE},
};

/*
 * configuration for one pin group.
 */
struct pincfg {
	char	*function;
	int	params[PROP_ID_MAX_ID];
};
#define	GPIO_BANK_A	 0
#define	GPIO_BANK_B	 1
#define	GPIO_BANK_C	 2
#define	GPIO_BANK_D	 3
#define	GPIO_BANK_E	 4
#define	GPIO_BANK_F	 5
#define	GPIO_BANK_G	 6
#define	GPIO_BANK_H	 7
#define	GPIO_BANK_I	 8
#define	GPIO_BANK_J	 9
#define	GPIO_BANK_K	10
#define	GPIO_BANK_L	11
#define	GPIO_BANK_M	12
#define	GPIO_BANK_N	13
#define	GPIO_BANK_O	14
#define	GPIO_BANK_P	15
#define	GPIO_BANK_Q	16
#define	GPIO_BANK_R	17
#define	GPIO_BANK_S	18
#define	GPIO_BANK_T	19
#define	GPIO_BANK_U	20
#define	GPIO_BANK_V	21
#define	GPIO_BANK_W	22
#define	GPIO_BANK_X	23
#define	GPIO_BANK_Y	24
#define	GPIO_BANK_Z	25
#define	GPIO_BANK_AA	26
#define	GPIO_BANK_BB	27
#define	GPIO_BANK_CC	28
#define	GPIO_BANK_DD	29
#define	GPIO_BANK_EE	30
#define	GPIO_NUM(b, p) (8 * (b) + (p))

struct tegra_grp {
	char *name;
	bus_size_t reg;
	int drvdn_shift;
	int drvdn_mask;
	int drvup_shift;
	int drvup_mask;
};

#define	GRP(r, nm, dn_s, dn_w, up_s, up_w)				\
{									\
	.name = #nm,							\
	.reg = r - 0x8D4,						\
	.drvdn_shift = dn_s,						\
	.drvdn_mask = (1 << dn_w) - 1,					\
	.drvup_shift = up_s,						\
	.drvup_mask = (1 << up_w) - 1,					\
}

/* Use register offsets from TRM */
static const struct tegra_grp pin_grp_tbl[] = {
	GRP(0x9c0,    pa6, 12,  5, 20,  5),
	GRP(0x9c4,   pcc7, 12,  5, 20,  5),
	GRP(0x9c8,    pe6, 12,  5, 20,  5),
	GRP(0x9cc,    pe7, 12,  5, 20,  5),
	GRP(0x9d0,    ph6, 12,  5, 20,  5),
	GRP(0x9d4,    pk0,  0,  0,  0,  0),
	GRP(0x9d8,    pk1,  0,  0,  0,  0),
	GRP(0x9dc,    pk2,  0,  0,  0,  0),
	GRP(0x9e0,    pk3,  0,  0,  0,  0),
	GRP(0x9e4,    pk4,  0,  0,  0,  0),
	GRP(0x9e8,    pk5,  0,  0,  0,  0),
	GRP(0x9ec,    pk6,  0,  0,  0,  0),
	GRP(0x9f0,    pk7,  0,  0,  0,  0),
	GRP(0x9f4,    pl0,  0,  0,  0,  0),
	GRP(0x9f8,    pl1,  0,  0,  0,  0),
	GRP(0x9fc,    pz0, 12,  7, 20,  7),
	GRP(0xa00,    pz1, 12,  7, 20,  7),
	GRP(0xa04,    pz2, 12,  7, 20,  7),
	GRP(0xa08,    pz3, 12,  7, 20,  7),
	GRP(0xa0c,    pz4, 12,  7, 20,  7),
	GRP(0xa10,    pz5, 12,  7, 20,  7),
	GRP(0xa98, sdmmc1, 12,  7, 20,  7),
	GRP(0xa9c, sdmmc2,  2,  6,  8,  6),
	GRP(0xab0, sdmmc3, 12,  7, 20,  7),
	GRP(0xab4, sdmmc4,  2,  6,  8,  6),
};

struct tegra_mux {
	struct tegra_grp grp;
	char *name;
	bus_size_t reg;
	char *functions[4];
	int gpio_num;

};

#define	GMUX(r, gb, gi, nm, f1, f2, f3, f4, gr, dn_s, dn_w, up_s, up_w)	\
{									\
	.name = #nm,							\
	.reg = r,							\
	.gpio_num = GPIO_NUM(GPIO_BANK_##gb, gi),			\
	.functions = {#f1, #f2, #f3, #f4},				\
	.grp.name = #nm,						\
	.grp.reg = gr - 0x8D4,						\
	.grp.drvdn_shift = dn_s,					\
	.grp.drvdn_mask = (1 << dn_w) - 1,				\
	.grp.drvup_shift = up_s,					\
	.grp.drvup_mask = (1 << up_w) - 1,				\
}

#define	FMUX(r, nm, f1, f2, f3, f4, gr, dn_s, dn_w, up_s, up_w)		\
{									\
	.name = #nm,							\
	.reg = r,							\
	.gpio_num = -1,							\
	.functions = {#f1, #f2, #f3, #f4},				\
	.grp.name = #nm,						\
	.grp.reg = gr - 0x8D4,						\
	.grp.drvdn_shift = dn_s,					\
	.grp.drvdn_mask = (1 << dn_w) - 1,				\
	.grp.drvup_shift = up_s,					\
	.grp.drvup_mask = (1 << up_w) - 1,				\
}

static const struct tegra_mux pin_mux_tbl[] = {
	GMUX(0x000,  M, 0, sdmmc1_clk_pm0,       sdmmc1,  rsvd1, rsvd2, rsvd3,      -1,  0,  0,  0,  0),
	GMUX(0x004,  M, 1, sdmmc1_cmd_pm1,       sdmmc1,   spi3, rsvd2, rsvd3,      -1,  0,  0,  0,  0),
	GMUX(0x008,  M, 2, sdmmc1_dat3_pm2,      sdmmc1,   spi3, rsvd2, rsvd3,      -1,  0,  0,  0,  0),
	GMUX(0x00c,  M, 3, sdmmc1_dat2_pm3,      sdmmc1,   spi3, rsvd2, rsvd3,      -1,  0,  0,  0,  0),
	GMUX(0x010,  M, 4, sdmmc1_dat1_pm4,      sdmmc1,   spi3, rsvd2, rsvd3,      -1,  0,  0,  0,  0),
	GMUX(0x014,  M, 5, sdmmc1_dat0_pm5,      sdmmc1,  rsvd1, rsvd2, rsvd3,      -1,  0,  0,  0,  0),
	GMUX(0x01c,  P, 0, sdmmc3_clk_pp0,       sdmmc3,  rsvd1, rsvd2, rsvd3,      -1,  0,  0,  0,  0),
	GMUX(0x020,  P, 1, sdmmc3_cmd_pp1,       sdmmc3,  rsvd1, rsvd2, rsvd3,      -1,  0,  0,  0,  0),
	GMUX(0x024,  P, 5, sdmmc3_dat0_pp5,      sdmmc3,  rsvd1, rsvd2, rsvd3,      -1,  0,  0,  0,  0),
	GMUX(0x028,  P, 4, sdmmc3_dat1_pp4,      sdmmc3,  rsvd1, rsvd2, rsvd3,      -1,  0,  0,  0,  0),
	GMUX(0x02c,  P, 3, sdmmc3_dat2_pp3,      sdmmc3,  rsvd1, rsvd2, rsvd3,      -1,  0,  0,  0,  0),
	GMUX(0x030,  P, 2, sdmmc3_dat3_pp2,      sdmmc3,  rsvd1, rsvd2, rsvd3,      -1,  0,  0,  0,  0),
	GMUX(0x038,  A, 0, pex_l0_rst_n_pa0,        pe0,  rsvd1, rsvd2, rsvd3,   0xa5c, 12,  5, 20,  5),
	GMUX(0x03c,  A, 1, pex_l0_clkreq_n_pa1,     pe0,  rsvd1, rsvd2, rsvd3,   0xa58, 12,  5, 20,  5),
	GMUX(0x040,  A, 2, pex_wake_n_pa2,           pe,  rsvd1, rsvd2, rsvd3,   0xa68, 12,  5, 20,  5),
	GMUX(0x044,  A, 3, pex_l1_rst_n_pa3,        pe1,  rsvd1, rsvd2, rsvd3,   0xa64, 12,  5, 20,  5),
	GMUX(0x048,  A, 4, pex_l1_clkreq_n_pa4,     pe1,  rsvd1, rsvd2, rsvd3,   0xa60, 12,  5, 20,  5),
	GMUX(0x04c,  A, 5, sata_led_active_pa5,    sata,  rsvd1, rsvd2, rsvd3,   0xa94, 12,  5, 20,  5),
	GMUX(0x050,  C, 0, spi1_mosi_pc0,          spi1,  rsvd1, rsvd2, rsvd3,   0xae0,  0,  0,  0,  0),
	GMUX(0x054,  C, 1, spi1_miso_pc1,          spi1,  rsvd1, rsvd2, rsvd3,   0xadc,  0,  0,  0,  0),
	GMUX(0x058,  C, 2, spi1_sck_pc2,           spi1,  rsvd1, rsvd2, rsvd3,   0xae4,  0,  0,  0,  0),
	GMUX(0x05c,  C, 3, spi1_cs0_pc3,           spi1,  rsvd1, rsvd2, rsvd3,   0xad4,  0,  0,  0,  0),
	GMUX(0x060,  C, 4, spi1_cs1_pc4,           spi1,  rsvd1, rsvd2, rsvd3,   0xad8,  0,  0,  0,  0),
	GMUX(0x064,  B, 4, spi2_mosi_pb4,          spi2,    dtv, rsvd2, rsvd3,   0xaf4,  0,  0,  0,  0),
	GMUX(0x068,  B, 5, spi2_miso_pb5,          spi2,    dtv, rsvd2, rsvd3,   0xaf0,  0,  0,  0,  0),
	GMUX(0x06c,  B, 6, spi2_sck_pb6,           spi2,    dtv, rsvd2, rsvd3,   0xaf8,  0,  0,  0,  0),
	GMUX(0x070,  B, 7, spi2_cs0_pb7,           spi2,    dtv, rsvd2, rsvd3,   0xae8,  0,  0,  0,  0),
	GMUX(0x074, DD, 0, spi2_cs1_pdd0,          spi2,  rsvd1, rsvd2, rsvd3,   0xaec,  0,  0,  0,  0),
	GMUX(0x078,  C, 7, spi4_mosi_pc7,          spi4,  rsvd1, rsvd2, rsvd3,   0xb04,  0,  0,  0,  0),
	GMUX(0x07c,  D, 0, spi4_miso_pd0,          spi4,  rsvd1, rsvd2, rsvd3,   0xb00,  0,  0,  0,  0),
	GMUX(0x080,  C, 5, spi4_sck_pc5,           spi4,  rsvd1, rsvd2, rsvd3,   0xb08,  0,  0,  0,  0),
	GMUX(0x084,  C, 6, spi4_cs0_pc6,           spi4,  rsvd1, rsvd2, rsvd3,   0xafc,  0,  0,  0,  0),
	GMUX(0x088, EE, 0, qspi_sck_pee0,          qspi,  rsvd1, rsvd2, rsvd3,   0xa90,  0,  0,  0,  0),
	GMUX(0x08c, EE, 1, qspi_cs_n_pee1,         qspi,  rsvd1, rsvd2, rsvd3,      -1,  0,  0,  0,  0),
	GMUX(0x090, EE, 2, qspi_io0_pee2,          qspi,  rsvd1, rsvd2, rsvd3,      -1,  0,  0,  0,  0),
	GMUX(0x094, EE, 3, qspi_io1_pee3,          qspi,  rsvd1, rsvd2, rsvd3,      -1,  0,  0,  0,  0),
	GMUX(0x098, EE, 4, qspi_io2_pee4,          qspi,  rsvd1, rsvd2, rsvd3,      -1,  0,  0,  0,  0),
	GMUX(0x09c, EE, 5, qspi_io3_pee5,          qspi,  rsvd1, rsvd2, rsvd3,      -1,  0,  0,  0,  0),
	GMUX(0x0a4,  E, 0, dmic1_clk_pe0,         dmic1,   i2s3, rsvd2, rsvd3,   0x984, 12,  5, 20,  5),
	GMUX(0x0a8,  E, 1, dmic1_dat_pe1,         dmic1,   i2s3, rsvd2, rsvd3,   0x988, 12,  5, 20,  5),
	GMUX(0x0ac,  E, 2, dmic2_clk_pe2,         dmic2,   i2s3, rsvd2, rsvd3,   0x98c, 12,  5, 20,  5),
	GMUX(0x0b0,  E, 3, dmic2_dat_pe3,         dmic2,   i2s3, rsvd2, rsvd3,   0x990, 12,  5, 20,  5),
	GMUX(0x0b4,  E, 4, dmic3_clk_pe4,         dmic3,  i2s5a, rsvd2, rsvd3,   0x994, 12,  5, 20,  5),
	GMUX(0x0b8,  E, 5, dmic3_dat_pe5,         dmic3,  i2s5a, rsvd2, rsvd3,   0x998, 12,  5, 20,  5),
	GMUX(0x0bc,  J, 1, gen1_i2c_scl_pj1,       i2c1,  rsvd1, rsvd2, rsvd3,   0x9a8, 12,  5, 20,  5),
	GMUX(0x0c0,  J, 0, gen1_i2c_sda_pj0,       i2c1,  rsvd1, rsvd2, rsvd3,   0x9ac, 12,  5, 20,  5),
	GMUX(0x0c4,  J, 2, gen2_i2c_scl_pj2,       i2c2,  rsvd1, rsvd2, rsvd3,   0x9b0, 12,  5, 20,  5),
	GMUX(0x0c8,  J, 3, gen2_i2c_sda_pj3,       i2c2,  rsvd1, rsvd2, rsvd3,   0x9b4, 12,  5, 20,  5),
	GMUX(0x0cc,  F, 0, gen3_i2c_scl_pf0,       i2c3,  rsvd1, rsvd2, rsvd3,   0x9b8, 12,  5, 20,  5),
	GMUX(0x0d0,  F, 1, gen3_i2c_sda_pf1,       i2c3,  rsvd1, rsvd2, rsvd3,   0x9bc, 12,  5, 20,  5),
	GMUX(0x0d4,  S, 2, cam_i2c_scl_ps2,        i2c3,  i2cvi, rsvd2, rsvd3,   0x934, 12,  5, 20,  5),
	GMUX(0x0d8,  S, 3, cam_i2c_sda_ps3,        i2c3,  i2cvi, rsvd2, rsvd3,   0x938, 12,  5, 20,  5),
	GMUX(0x0dc,  Y, 3, pwr_i2c_scl_py3,      i2cpmu,  rsvd1, rsvd2, rsvd3,   0xa6c, 12,  5, 20,  5),
	GMUX(0x0e0,  Y, 4, pwr_i2c_sda_py4,      i2cpmu,  rsvd1, rsvd2, rsvd3,   0xa70, 12,  5, 20,  5),
	GMUX(0x0e4,  U, 0, uart1_tx_pu0,          uarta,  rsvd1, rsvd2, rsvd3,   0xb28, 12,  5, 20,  5),
	GMUX(0x0e8,  U, 1, uart1_rx_pu1,          uarta,  rsvd1, rsvd2, rsvd3,   0xb24, 12,  5, 20,  5),
	GMUX(0x0ec,  U, 2, uart1_rts_pu2,         uarta,  rsvd1, rsvd2, rsvd3,   0xb20, 12,  5, 20,  5),
	GMUX(0x0f0,  U, 3, uart1_cts_pu3,         uarta,  rsvd1, rsvd2, rsvd3,   0xb1c, 12,  5, 20,  5),
	GMUX(0x0f4,  G, 0, uart2_tx_pg0,          uartb,  i2s4a, spdif,  uart,   0xb38, 12,  5, 20,  5),
	GMUX(0x0f8,  G, 1, uart2_rx_pg1,          uartb,  i2s4a, spdif,  uart,   0xb34, 12,  5, 20,  5),
	GMUX(0x0fc,  G, 2, uart2_rts_pg2,         uartb,  i2s4a, rsvd2,  uart,   0xb30, 12,  5, 20,  5),
	GMUX(0x100,  G, 3, uart2_cts_pg3,         uartb,  i2s4a, rsvd2,  uart,   0xb2c, 12,  5, 20,  5),
	GMUX(0x104,  D, 1, uart3_tx_pd1,          uartc,   spi4, rsvd2, rsvd3,   0xb48, 12,  5, 20,  5),
	GMUX(0x108,  D, 2, uart3_rx_pd2,          uartc,   spi4, rsvd2, rsvd3,   0xb44, 12,  5, 20,  5),
	GMUX(0x10c,  D, 3, uart3_rts_pd3,         uartc,   spi4, rsvd2, rsvd3,   0xb40, 12,  5, 20,  5),
	GMUX(0x110,  D, 4, uart3_cts_pd4,         uartc,   spi4, rsvd2, rsvd3,   0xb3c, 12,  5, 20,  5),
	GMUX(0x114,  I, 4, uart4_tx_pi4,          uartd,   uart, rsvd2, rsvd3,   0xb58, 12,  5, 20,  5),
	GMUX(0x118,  I, 5, uart4_rx_pi5,          uartd,   uart, rsvd2, rsvd3,   0xb54, 12,  5, 20,  5),
	GMUX(0x11c,  I, 6, uart4_rts_pi6,         uartd,   uart, rsvd2, rsvd3,   0xb50, 12,  5, 20,  5),
	GMUX(0x120,  I, 7, uart4_cts_pi7,         uartd,   uart, rsvd2, rsvd3,   0xb4c, 12,  5, 20,  5),
	GMUX(0x124,  B, 0, dap1_fs_pb0,            i2s1,  rsvd1, rsvd2, rsvd3,   0x95c,  0,  0,  0,  0),
	GMUX(0x128,  B, 1, dap1_din_pb1,           i2s1,  rsvd1, rsvd2, rsvd3,   0x954,  0,  0,  0,  0),
	GMUX(0x12c,  B, 2, dap1_dout_pb2,          i2s1,  rsvd1, rsvd2, rsvd3,   0x958,  0,  0,  0,  0),
	GMUX(0x130,  B, 3, dap1_sclk_pb3,          i2s1,  rsvd1, rsvd2, rsvd3,   0x960,  0,  0,  0,  0),
	GMUX(0x134, AA, 0, dap2_fs_paa0,           i2s2,  rsvd1, rsvd2, rsvd3,   0x96c,  0,  0,  0,  0),
	GMUX(0x138, AA, 2, dap2_din_paa2,          i2s2,  rsvd1, rsvd2, rsvd3,   0x964,  0,  0,  0,  0),
	GMUX(0x13c, AA, 3, dap2_dout_paa3,         i2s2,  rsvd1, rsvd2, rsvd3,   0x968,  0,  0,  0,  0),
	GMUX(0x140, AA, 1, dap2_sclk_paa1,         i2s2,  rsvd1, rsvd2, rsvd3,   0x970,  0,  0,  0,  0),
	GMUX(0x144,  J, 4, dap4_fs_pj4,           i2s4b,  rsvd1, rsvd2, rsvd3,   0x97c, 12,  5, 20,  5),
	GMUX(0x148,  J, 5, dap4_din_pj5,          i2s4b,  rsvd1, rsvd2, rsvd3,   0x974, 12,  5, 20,  5),
	GMUX(0x14c,  J, 6, dap4_dout_pj6,         i2s4b,  rsvd1, rsvd2, rsvd3,   0x978, 12,  5, 20,  5),
	GMUX(0x150,  J, 7, dap4_sclk_pj7,         i2s4b,  rsvd1, rsvd2, rsvd3,   0x980, 12,  5, 20,  5),
	GMUX(0x154,  S, 0, cam1_mclk_ps0,    extperiph3,  rsvd1, rsvd2, rsvd3,   0x918, 12,  5, 20,  5),
	GMUX(0x158,  S, 1, cam2_mclk_ps1,    extperiph3,  rsvd1, rsvd2, rsvd3,   0x924, 12,  5, 20,  5),
	FMUX(0x15c,        jtag_rtck,              jtag,  rsvd1, rsvd2, rsvd3,   0xa2c, 12,  5, 20,  5),
	FMUX(0x160,        clk_32k_in,              clk,  rsvd1, rsvd2, rsvd3,   0x940, 12,  5, 20,  5),
	GMUX(0x164,  Y, 5, clk_32k_out_py5,         soc,  blink, rsvd2, rsvd3,   0x944, 12,  5, 20,  5),
	FMUX(0x168,        batt_bcl,                bcl,  rsvd1, rsvd2, rsvd3,   0x8f8, 12,  5, 20,  5),
	FMUX(0x16c,        clk_req,                 sys,  rsvd1, rsvd2, rsvd3,   0x948, 12,  5, 20,  5),
	FMUX(0x170,        cpu_pwr_req,             cpu,  rsvd1, rsvd2, rsvd3,   0x950, 12,  5, 20,  5),
	FMUX(0x174,        pwr_int_n,               pmi,  rsvd1, rsvd2, rsvd3,   0xa74, 12,  5, 20,  5),
	FMUX(0x178,        shutdown,           shutdown,  rsvd1, rsvd2, rsvd3,   0xac8, 12,  5, 20,  5),
	FMUX(0x17c,        core_pwr_req,           core,  rsvd1, rsvd2, rsvd3,   0x94c, 12,  5, 20,  5),
	GMUX(0x180, BB, 0, aud_mclk_pbb0,           aud,  rsvd1, rsvd2, rsvd3,   0x8f4, 12,  5, 20,  5),
	GMUX(0x184, BB, 1, dvfs_pwm_pbb1,         rsvd0, cldvfs,  spi3, rsvd3,   0x9a4, 12,  5, 20,  5),
	GMUX(0x188, BB, 2, dvfs_clk_pbb2,         rsvd0, cldvfs,  spi3, rsvd3,   0x9a0, 12,  5, 20,  5),
	GMUX(0x18c, BB, 3, gpio_x1_aud_pbb3,      rsvd0,  rsvd1,  spi3, rsvd3,   0xa14, 12,  5, 20,  5),
	GMUX(0x190, BB, 4, gpio_x3_aud_pbb4,      rsvd0,  rsvd1,  spi3, rsvd3,   0xa18, 12,  5, 20,  5),
	GMUX(0x194, CC, 7, pcc7,                  rsvd0,  rsvd1, rsvd2, rsvd3,      -1,  0,  0,  0,  0),
	GMUX(0x198, CC, 0, hdmi_cec_pcc0,           cec,  rsvd1, rsvd2, rsvd3,   0xa24, 12,  5, 20,  5),
	GMUX(0x19c, CC, 1, hdmi_int_dp_hpd_pcc1,     dp,  rsvd1, rsvd2, rsvd3,   0xa28, 12,  5, 20,  5),
	GMUX(0x1a0, CC, 2, spdif_out_pcc2,        spdif,  rsvd1, rsvd2, rsvd3,   0xad0, 12,  5, 20,  5),
	GMUX(0x1a4, CC, 3, spdif_in_pcc3,         spdif,  rsvd1, rsvd2, rsvd3,   0xacc, 12,  5, 20,  5),
	GMUX(0x1a8, CC, 4, usb_vbus_en0_pcc4,       usb,  rsvd1, rsvd2, rsvd3,   0xb5c, 12,  5, 20,  5),
	GMUX(0x1ac, CC, 5, usb_vbus_en1_pcc5,       usb,  rsvd1, rsvd2, rsvd3,   0xb60, 12,  5, 20,  5),
	GMUX(0x1b0, CC, 6, dp_hpd0_pcc6,             dp,  rsvd1, rsvd2, rsvd3,   0x99c, 12,  5, 20,  5),
	GMUX(0x1b4,  H, 0, wifi_en_ph0,           rsvd0,  rsvd1, rsvd2, rsvd3,   0xb64, 12,  5, 20,  5),
	GMUX(0x1b8,  H, 1, wifi_rst_ph1,          rsvd0,  rsvd1, rsvd2, rsvd3,   0xb68, 12,  5, 20,  5),
	GMUX(0x1bc,  H, 2, wifi_wake_ap_ph2,      rsvd0,  rsvd1, rsvd2, rsvd3,   0xb6c, 12,  5, 20,  5),
	GMUX(0x1c0,  H, 3, ap_wake_bt_ph3,        rsvd0,  uartb, spdif, rsvd3,   0x8ec, 12,  5, 20,  5),
	GMUX(0x1c4,  H, 4, bt_rst_ph4,            rsvd0,  uartb, spdif, rsvd3,   0x8fc, 12,  5, 20,  5),
	GMUX(0x1c8,  H, 5, bt_wake_ap_ph5,        rsvd0,  rsvd1, rsvd2, rsvd3,   0x900, 12,  5, 20,  5),
	GMUX(0x1cc,  H, 7, ap_wake_nfc_ph7,       rsvd0,  rsvd1, rsvd2, rsvd3,   0x8f0, 12,  5, 20,  5),
	GMUX(0x1d0,  I, 0, nfc_en_pi0,            rsvd0,  rsvd1, rsvd2, rsvd3,   0xa50, 12,  5, 20,  5),
	GMUX(0x1d4,  I, 1, nfc_int_pi1,           rsvd0,  rsvd1, rsvd2, rsvd3,   0xa54, 12,  5, 20,  5),
	GMUX(0x1d8,  I, 2, gps_en_pi2,            rsvd0,  rsvd1, rsvd2, rsvd3,   0xa1c, 12,  5, 20,  5),
	GMUX(0x1dc,  I, 3, gps_rst_pi3,           rsvd0,  rsvd1, rsvd2, rsvd3,   0xa20, 12,  5, 20,  5),
	GMUX(0x1e0,  S, 4, cam_rst_ps4,            vgp1,  rsvd1, rsvd2, rsvd3,   0x93c, 12,  5, 20,  5),
	GMUX(0x1e4,  S, 5, cam_af_en_ps5,        vimclk,   vgp2, rsvd2, rsvd3,   0x92c, 12,  5, 20,  5),
	GMUX(0x1e8,  S, 6, cam_flash_en_ps6,     vimclk,   vgp3, rsvd2, rsvd3,   0x930, 12,  5, 20,  5),
	GMUX(0x1ec,  S, 7, cam1_pwdn_ps7,          vgp4,  rsvd1, rsvd2, rsvd3,   0x91c, 12,  5, 20,  5),
	GMUX(0x1f0,  T, 0, cam2_pwdn_pt0,          vgp5,  rsvd1, rsvd2, rsvd3,   0x928, 12,  5, 20,  5),
	GMUX(0x1f4,  T, 1, cam1_strobe_pt1,        vgp6,  rsvd1, rsvd2, rsvd3,   0x920, 12,  5, 20,  5),
	GMUX(0x1f8,  Y, 2, lcd_te_py2,         displaya,  rsvd1, rsvd2, rsvd3,   0xa44, 12,  5, 20,  5),
	GMUX(0x1fc,  V, 0, lcd_bl_pwm_pv0,     displaya,   pwm0,  sor0, rsvd3,   0xa34, 12,  5, 20,  5),
	GMUX(0x200,  V, 1, lcd_bl_en_pv1,         rsvd0,  rsvd1, rsvd2, rsvd3,   0xa30, 12,  5, 20,  5),
	GMUX(0x204,  V, 2, lcd_rst_pv2,           rsvd0,  rsvd1, rsvd2, rsvd3,   0xa40, 12,  5, 20,  5),
	GMUX(0x208,  V, 3, lcd_gpio1_pv3,      displayb,  rsvd1, rsvd2, rsvd3,   0xa38, 12,  5, 20,  5),
	GMUX(0x20c,  V, 4, lcd_gpio2_pv4,      displayb,   pwm1, rsvd2,  sor1,   0xa3c, 12,  5, 20,  5),
	GMUX(0x210,  V, 5, ap_ready_pv5,          rsvd0,  rsvd1, rsvd2, rsvd3,   0x8e8, 12,  5, 20,  5),
	GMUX(0x214,  V, 6, touch_rst_pv6,         rsvd0,  rsvd1, rsvd2, rsvd3,   0xb18, 12,  5, 20,  5),
	GMUX(0x218,  V, 7, touch_clk_pv7,         touch,  rsvd1, rsvd2, rsvd3,   0xb10, 12,  5, 20,  5),
	GMUX(0x21c,  X, 0, modem_wake_ap_px0,     rsvd0,  rsvd1, rsvd2, rsvd3,   0xa48, 12,  5, 20,  5),
	GMUX(0x220,  X, 1, touch_int_px1,         rsvd0,  rsvd1, rsvd2, rsvd3,   0xb14, 12,  5, 20,  5),
	GMUX(0x224,  X, 2, motion_int_px2,        rsvd0,  rsvd1, rsvd2, rsvd3,   0xa4c, 12,  5, 20,  5),
	GMUX(0x228,  X, 3, als_prox_int_px3,      rsvd0,  rsvd1, rsvd2, rsvd3,   0x8e4, 12,  5, 20,  5),
	GMUX(0x22c,  X, 4, temp_alert_px4,        rsvd0,  rsvd1, rsvd2, rsvd3,   0xb0c, 12,  5, 20,  5),
	GMUX(0x230,  X, 5, button_power_on_px5,   rsvd0,  rsvd1, rsvd2, rsvd3,   0x908, 12,  5, 20,  5),
	GMUX(0x234,  X, 6, button_vol_up_px6,     rsvd0,  rsvd1, rsvd2, rsvd3,   0x914, 12,  5, 20,  5),
	GMUX(0x238,  X, 7, button_vol_down_px7,   rsvd0,  rsvd1, rsvd2, rsvd3,   0x910, 12,  5, 20,  5),
	GMUX(0x23c,  Y, 0, button_slide_sw_py0,   rsvd0,  rsvd1, rsvd2, rsvd3,   0x90c, 12,  5, 20,  5),
	GMUX(0x240,  Y, 1, button_home_py1,       rsvd0,  rsvd1, rsvd2, rsvd3,   0x904, 12,  5, 20,  5),
	GMUX(0x244,  A, 6, pa6,                   sata,   rsvd1, rsvd2, rsvd3,      -1,  0,  0,  0,  0),
	GMUX(0x248,  E, 6, pe6,                   rsvd0,  i2s5a,  pwm2, rsvd3,      -1,  0,  0,  0,  0),
	GMUX(0x24c,  E, 7, pe7,                   rsvd0,  i2s5a,  pwm3, rsvd3,      -1,  0,  0,  0,  0),
	GMUX(0x250,  H, 6, ph6,                   rsvd0,  rsvd1, rsvd2, rsvd3,      -1,  0,  0,  0,  0),
	GMUX(0x254,  K, 0, pk0,                   iqc0,   i2s5b, rsvd2, rsvd3,      -1,  0,  0,  0,  0),
	GMUX(0x258,  K, 1, pk1,                   iqc0,   i2s5b, rsvd2, rsvd3,      -1,  0,  0,  0,  0),
	GMUX(0x25c,  K, 2, pk2,                   iqc0,   i2s5b, rsvd2, rsvd3,      -1,  0,  0,  0,  0),
	GMUX(0x260,  K, 3, pk3,                   iqc0,   i2s5b, rsvd2, rsvd3,      -1,  0,  0,  0,  0),
	GMUX(0x264,  K, 4, pk4,                   iqc1,   rsvd1, rsvd2, rsvd3,      -1,  0,  0,  0,  0),
	GMUX(0x268,  K, 5, pk5,                   iqc1,   rsvd1, rsvd2, rsvd3,      -1,  0,  0,  0,  0),
	GMUX(0x26c,  K, 6, pk6,                   iqc1,   rsvd1, rsvd2, rsvd3,      -1,  0,  0,  0,  0),
	GMUX(0x270,  K, 7, pk7,                   iqc1,   rsvd1, rsvd2, rsvd3,      -1,  0,  0,  0,  0),
	GMUX(0x274,  L, 0, pl0,                  rsvd0,   rsvd1, rsvd2, rsvd3,      -1,  0,  0,  0,  0),
	GMUX(0x278,  L, 1, pl1,                    soc,   rsvd1, rsvd2, rsvd3,      -1,  0,  0,  0,  0),
	GMUX(0x27c,  Z, 0, pz0,                vimclk2,   rsvd1, rsvd2, rsvd3,      -1,  0,  0,  0,  0),
	GMUX(0x280,  Z, 1, pz1,                vimclk2,  sdmmc1, rsvd2, rsvd3,      -1,  0,  0,  0,  0),
	GMUX(0x284,  Z, 2, pz2,                 sdmmc3,    ccla, rsvd2, rsvd3,      -1,  0,  0,  0,  0),
	GMUX(0x288,  Z, 3, pz3,                 sdmmc3,   rsvd1, rsvd2, rsvd3,      -1,  0,  0,  0,  0),
	GMUX(0x28c,  Z, 4, pz4,                 sdmmc1,   rsvd1, rsvd2, rsvd3,      -1,  0,  0,  0,  0),
	GMUX(0x290,  Z, 5, pz5,                    soc,   rsvd1, rsvd2, rsvd3,      -1,  0,  0,  0,  0),
};


static const struct tegra_grp *
pinmux_search_grp(char *grp_name)
{
	int i;

	for (i = 0; i < nitems(pin_grp_tbl); i++) {
		if (strcmp(grp_name, pin_grp_tbl[i].name) == 0)
			return 	(&pin_grp_tbl[i]);
	}
	return (NULL);
}

static const struct tegra_mux *
pinmux_search_mux(char *pin_name)
{
	int i;

	for (i = 0; i < nitems(pin_mux_tbl); i++) {
		if (strcmp(pin_name, pin_mux_tbl[i].name) == 0)
			return 	(&pin_mux_tbl[i]);
	}
	return (NULL);
}

static int
pinmux_mux_function(const struct tegra_mux *mux, char *fnc_name)
{
	int i;

	for (i = 0; i < 4; i++) {
		if (strcmp(fnc_name, mux->functions[i]) == 0)
			return 	(i);
	}
	return (-1);
}

static int
pinmux_config_mux(struct pinmux_softc *sc, char *pin_name,
    const struct tegra_mux *mux, struct pincfg *cfg)
{
	int tmp;
	uint32_t reg;

	reg = bus_read_4(sc->mux_mem_res, mux->reg);

	if (cfg->function != NULL) {
		tmp = pinmux_mux_function(mux, cfg->function);
		if (tmp == -1) {
			device_printf(sc->dev,
			    "Unknown function %s for pin %s\n", cfg->function,
			    pin_name);
			return (ENXIO);
		}
		reg &= ~(TEGRA_MUX_FUNCTION_MASK << TEGRA_MUX_FUNCTION_SHIFT);
		reg |=  (tmp & TEGRA_MUX_FUNCTION_MASK) <<
		    TEGRA_MUX_FUNCTION_SHIFT;
	}
	if (cfg->params[PROP_ID_PULL] != -1) {
		reg &= ~(TEGRA_MUX_PUPD_MASK << TEGRA_MUX_PUPD_SHIFT);
		reg |=  (cfg->params[PROP_ID_PULL] & TEGRA_MUX_PUPD_MASK) <<
		    TEGRA_MUX_PUPD_SHIFT;
	}
	if (cfg->params[PROP_ID_TRISTATE] != -1) {
		reg &= ~(1 << TEGRA_MUX_TRISTATE_SHIFT);
		reg |=  (cfg->params[PROP_ID_TRISTATE] & 1) <<
		    TEGRA_MUX_TRISTATE_SHIFT;
	}
	if (cfg->params[TEGRA_MUX_ENABLE_INPUT_SHIFT] != -1) {
		reg &= ~(1 << TEGRA_MUX_ENABLE_INPUT_SHIFT);
		reg |=  (cfg->params[TEGRA_MUX_ENABLE_INPUT_SHIFT] & 1) <<
		    TEGRA_MUX_ENABLE_INPUT_SHIFT;
	}
	if (cfg->params[PROP_ID_ENABLE_INPUT] != -1) {
		reg &= ~(1 << TEGRA_MUX_ENABLE_INPUT_SHIFT);
		reg |=  (cfg->params[PROP_ID_ENABLE_INPUT] & 1) <<
		    TEGRA_MUX_ENABLE_INPUT_SHIFT;
	}
	if (cfg->params[PROP_ID_ENABLE_INPUT] != -1) {
		reg &= ~(1 << TEGRA_MUX_ENABLE_INPUT_SHIFT);
		reg |=  (cfg->params[PROP_ID_OPEN_DRAIN] & 1) <<
		    TEGRA_MUX_ENABLE_INPUT_SHIFT;
	}
	if (cfg->params[PROP_ID_LOCK] != -1) {
		reg &= ~(1 << TEGRA_MUX_LOCK_SHIFT);
		reg |=  (cfg->params[PROP_ID_LOCK] & 1) <<
		    TEGRA_MUX_LOCK_SHIFT;
	}
	if (cfg->params[PROP_ID_IORESET] != -1) {
		reg &= ~(1 << TEGRA_MUX_IORESET_SHIFT);
		reg |=  (cfg->params[PROP_ID_IORESET] & 1) <<
		    TEGRA_MUX_IORESET_SHIFT;
	}
	if (cfg->params[PROP_ID_RCV_SEL] != -1) {
		reg &= ~(1 << TEGRA_MUX_RCV_SEL_SHIFT);
		reg |=  (cfg->params[PROP_ID_RCV_SEL] & 1) <<
		    TEGRA_MUX_RCV_SEL_SHIFT;
	}
	bus_write_4(sc->mux_mem_res, mux->reg, reg);
	return (0);
}

static int
pinmux_config_grp(struct pinmux_softc *sc, char *grp_name,
    const struct tegra_grp *grp, struct pincfg *cfg)
{
	uint32_t reg;

	reg = bus_read_4(sc->pad_mem_res, grp->reg);

	if (cfg->params[PROP_ID_HIGH_SPEED_MODE] != -1) {
		reg &= ~(1 << TEGRA_GRP_HSM_SHIFT);
		reg |=  (cfg->params[PROP_ID_HIGH_SPEED_MODE] & 1) <<
		    TEGRA_GRP_HSM_SHIFT;
	}
	if (cfg->params[PROP_ID_SCHMITT] != -1) {
		reg &= ~(1 << TEGRA_GRP_SCHMT_SHIFT);
		reg |=  (cfg->params[PROP_ID_SCHMITT] & 1) <<
		    TEGRA_GRP_SCHMT_SHIFT;
	}
	if (cfg->params[PROP_ID_DRIVE_TYPE] != -1) {
		reg &= ~(TEGRA_GRP_DRV_TYPE_MASK << TEGRA_GRP_DRV_TYPE_SHIFT);
		reg |=  (cfg->params[PROP_ID_DRIVE_TYPE] &
		    TEGRA_GRP_DRV_TYPE_MASK) << TEGRA_GRP_DRV_TYPE_SHIFT;
	}
	if (cfg->params[PROP_ID_SLEW_RATE_RISING] != -1) {
		reg &= ~(TEGRA_GRP_DRV_DRVDN_SLWR_MASK <<
		    TEGRA_GRP_DRV_DRVDN_SLWR_SHIFT);
		reg |=  (cfg->params[PROP_ID_SLEW_RATE_RISING] &
		    TEGRA_GRP_DRV_DRVDN_SLWR_MASK) <<
		    TEGRA_GRP_DRV_DRVDN_SLWR_SHIFT;
	}
	if (cfg->params[PROP_ID_SLEW_RATE_FALLING] != -1) {
		reg &= ~(TEGRA_GRP_DRV_DRVUP_SLWF_MASK <<
		    TEGRA_GRP_DRV_DRVUP_SLWF_SHIFT);
		reg |=  (cfg->params[PROP_ID_SLEW_RATE_FALLING] &
		    TEGRA_GRP_DRV_DRVUP_SLWF_MASK) <<
		    TEGRA_GRP_DRV_DRVUP_SLWF_SHIFT;
	}
	if ((cfg->params[PROP_ID_DRIVE_DOWN_STRENGTH] != -1) &&
		 (grp->drvdn_mask != 0)) {
		reg &= ~(grp->drvdn_shift << grp->drvdn_mask);
		reg |=  (cfg->params[PROP_ID_DRIVE_DOWN_STRENGTH] &
		    grp->drvdn_mask) << grp->drvdn_shift;
	}
	if ((cfg->params[PROP_ID_DRIVE_UP_STRENGTH] != -1) &&
		 (grp->drvup_mask != 0)) {
		reg &= ~(grp->drvup_shift << grp->drvup_mask);
		reg |=  (cfg->params[PROP_ID_DRIVE_UP_STRENGTH] &
		    grp->drvup_mask) << grp->drvup_shift;
	}
	bus_write_4(sc->pad_mem_res, grp->reg, reg);
	return (0);
}

static int
pinmux_config_node(struct pinmux_softc *sc, char *pin_name, struct pincfg *cfg)
{
	const struct tegra_mux *mux;
	const struct tegra_grp *grp;
	bool handled;
	int rv;

	/* Handle pin muxes */
	mux = pinmux_search_mux(pin_name);
	handled = false;
	if (mux != NULL) {
		if (mux->gpio_num != -1) {
			/* XXXX TODO: Reserve gpio here */
		}
		rv = pinmux_config_mux(sc, pin_name, mux, cfg);
		if (rv != 0)
			return (rv);
		if (mux->grp.reg <= 0) {
			rv = pinmux_config_grp(sc, pin_name, &mux->grp, cfg);
			return (rv);
		}
		handled = true;
	}

	/* And/or handle pin groups */
	grp = pinmux_search_grp(pin_name);
	if (grp != NULL) {
		rv = pinmux_config_grp(sc, pin_name, grp, cfg);
		if (rv != 0)
			return (rv);
		handled = true;
	}

	if (!handled) {
		device_printf(sc->dev, "Unknown pin: %s\n", pin_name);
		return (ENXIO);
	}
	return (0);
}

static int
pinmux_read_node(struct pinmux_softc *sc, phandle_t node, struct pincfg *cfg,
    char **pins, int *lpins)
{
	int rv, i;

	*lpins = OF_getprop_alloc(node, "nvidia,pins", (void **)pins);
	if (*lpins <= 0)
		return (ENOENT);

	/* Read function (mux) settings. */
	rv = OF_getprop_alloc(node, "nvidia,function", (void **)&cfg->function);
	if (rv <= 0)
		cfg->function = NULL;

	/* Read numeric properties. */
	for (i = 0; i < PROP_ID_MAX_ID; i++) {
		rv = OF_getencprop(node, prop_names[i].name, &cfg->params[i],
		    sizeof(cfg->params[i]));
		if (rv <= 0)
			cfg->params[i] = -1;
	}
	return (0);
}

static int
pinmux_process_node(struct pinmux_softc *sc, phandle_t node)
{
	struct pincfg cfg;
	char *pins, *pname;
	int i, len, lpins, rv;

	rv = pinmux_read_node(sc, node, &cfg, &pins, &lpins);
	if (rv != 0)
		return (rv);

	len = 0;
	pname = pins;
	do {
		i = strlen(pname) + 1;
		rv = pinmux_config_node(sc, pname, &cfg);
		if (rv != 0)
			device_printf(sc->dev, "Cannot configure pin: %s: %d\n",
			    pname, rv);
		len += i;
		pname += i;
	} while (len < lpins);

	if (pins != NULL)
		OF_prop_free(pins);
	if (cfg.function != NULL)
		OF_prop_free(cfg.function);
	return (rv);
}

static int pinmux_configure(device_t dev, phandle_t cfgxref)
{
	struct pinmux_softc *sc;
	phandle_t node, cfgnode;

	sc = device_get_softc(dev);
	cfgnode = OF_node_from_xref(cfgxref);


	for (node = OF_child(cfgnode); node != 0; node = OF_peer(node)) {
		if (!ofw_bus_node_status_okay(node))
			continue;
		pinmux_process_node(sc, node);
	}
	return (0);
}

static int
pinmux_probe(device_t dev)
{

	if (!ofw_bus_status_okay(dev))
		return (ENXIO);

	if (!ofw_bus_search_compatible(dev, compat_data)->ocd_data)
		return (ENXIO);

	device_set_desc(dev, "Tegra pin configuration");
	return (BUS_PROBE_DEFAULT);
}

static int
pinmux_detach(device_t dev)
{

	/* This device is always present. */
	return (EBUSY);
}

static int
pinmux_attach(device_t dev)
{
	struct pinmux_softc * sc;
	int rid;

	sc = device_get_softc(dev);
	sc->dev = dev;

	rid = 0;
	sc->pad_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
	    RF_ACTIVE);
	if (sc->pad_mem_res == NULL) {
		device_printf(dev, "Cannot allocate memory resources\n");
		return (ENXIO);
	}

	rid = 1;
	sc->mux_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
	    RF_ACTIVE);
	if (sc->mux_mem_res == NULL) {
		device_printf(dev, "Cannot allocate memory resources\n");
		return (ENXIO);
	}


	/* Register as a pinctrl device and process default configuration */
	fdt_pinctrl_register(dev, NULL);
	fdt_pinctrl_configure_by_name(dev, "boot");

	return (0);
}


static device_method_t tegra210_pinmux_methods[] = {
	/* Device interface */
	DEVMETHOD(device_probe,         pinmux_probe),
	DEVMETHOD(device_attach,        pinmux_attach),
	DEVMETHOD(device_detach,        pinmux_detach),

	/* fdt_pinctrl interface */
	DEVMETHOD(fdt_pinctrl_configure,pinmux_configure),

	DEVMETHOD_END
};

static DEFINE_CLASS_0(pinmux, tegra210_pinmux_driver, tegra210_pinmux_methods,
    sizeof(struct pinmux_softc));
EARLY_DRIVER_MODULE(tegra210_pinmux, simplebus, tegra210_pinmux_driver,
    NULL, NULL, 71);
