netbsd/sys/arch/arm/nvidia/tegra_io.c
2015-10-15 10:25:28 +02:00

222 lines
7.3 KiB
C

/* $NetBSD: tegra_io.c,v 1.13 2015/08/01 21:20:11 jmcneill Exp $ */
/*-
* Copyright (c) 2015 Jared D. McNeill <jmcneill@invisible.ca>
* 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 ``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 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_tegra.h"
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: tegra_io.c,v 1.13 2015/08/01 21:20:11 jmcneill Exp $");
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/device.h>
#include <machine/cpu.h>
#include <sys/bus.h>
#include <arm/mainbus/mainbus.h>
#include <arm/nvidia/tegra_reg.h>
#include <arm/nvidia/tegra_var.h>
#include "locators.h"
static int tegraio_match(device_t, cfdata_t, void *);
static void tegraio_attach(device_t, device_t, void *);
CFATTACH_DECL_NEW(tegra_io, 0,
tegraio_match, tegraio_attach, NULL, NULL);
static int tegraio_print(void *, const char *);
static int tegraio_find(device_t, cfdata_t, const int *, void *);
static void tegraio_scan(device_t, bus_space_handle_t,
const struct tegra_locators *, u_int);
static bool tegraio_found = false;
#define NOPORT TEGRAIOCF_PORT_DEFAULT
#define NOINTR TEGRAIO_INTR_DEFAULT
static const struct tegra_locators tegra_ppsb_locators[] = {
{ "tegracar",
TEGRA_CAR_OFFSET, TEGRA_CAR_SIZE, NOPORT, NOINTR },
{ "tegragpio",
TEGRA_GPIO_OFFSET, TEGRA_GPIO_SIZE, NOPORT, NOINTR },
{ "tegratimer",
TEGRA_TIMER_OFFSET, TEGRA_TIMER_SIZE, NOPORT, NOINTR },
};
static const struct tegra_locators tegra_apb_locators[] = {
{ "tegramc",
TEGRA_MC_OFFSET, TEGRA_MC_SIZE, NOPORT, NOINTR },
{ "tegrapmc",
TEGRA_PMC_OFFSET, TEGRA_PMC_SIZE, NOPORT, NOINTR },
{ "tegraxusbpad",
TEGRA_XUSB_PADCTL_OFFSET, TEGRA_XUSB_PADCTL_SIZE, NOPORT, NOINTR },
{ "tegrampio",
TEGRA_MPIO_OFFSET, TEGRA_MPIO_SIZE, NOPORT, NOINTR },
{ "tegrai2c",
TEGRA_I2C1_OFFSET, TEGRA_I2C1_SIZE, 0, TEGRA_INTR_I2C1 },
{ "tegrai2c",
TEGRA_I2C2_OFFSET, TEGRA_I2C2_SIZE, 1, TEGRA_INTR_I2C2 },
{ "tegrai2c",
TEGRA_I2C3_OFFSET, TEGRA_I2C3_SIZE, 2, TEGRA_INTR_I2C3 },
{ "tegrai2c",
TEGRA_I2C4_OFFSET, TEGRA_I2C4_SIZE, 3, TEGRA_INTR_I2C4 },
{ "tegrai2c",
TEGRA_I2C5_OFFSET, TEGRA_I2C5_SIZE, 4, TEGRA_INTR_I2C5 },
{ "tegrai2c",
TEGRA_I2C6_OFFSET, TEGRA_I2C6_SIZE, 5, TEGRA_INTR_I2C6 },
{ "com",
TEGRA_UARTA_OFFSET, TEGRA_UARTA_SIZE, 0, TEGRA_INTR_UARTA },
{ "com",
TEGRA_UARTB_OFFSET, TEGRA_UARTB_SIZE, 1, TEGRA_INTR_UARTB },
{ "com",
TEGRA_UARTC_OFFSET, TEGRA_UARTC_SIZE, 2, TEGRA_INTR_UARTC },
{ "com",
TEGRA_UARTD_OFFSET, TEGRA_UARTD_SIZE, 3, TEGRA_INTR_UARTD },
{ "tegrartc",
TEGRA_RTC_OFFSET, TEGRA_RTC_SIZE, NOPORT, NOINTR },
{ "sdhc",
TEGRA_SDMMC1_OFFSET, TEGRA_SDMMC1_SIZE, 0, TEGRA_INTR_SDMMC1 },
{ "sdhc",
TEGRA_SDMMC2_OFFSET, TEGRA_SDMMC2_SIZE, 1, TEGRA_INTR_SDMMC2 },
{ "sdhc",
TEGRA_SDMMC3_OFFSET, TEGRA_SDMMC3_SIZE, 2, TEGRA_INTR_SDMMC3 },
{ "sdhc",
TEGRA_SDMMC4_OFFSET, TEGRA_SDMMC4_SIZE, 3, TEGRA_INTR_SDMMC4 },
{ "ahcisata",
TEGRA_SATA_OFFSET, TEGRA_SATA_SIZE, NOPORT, TEGRA_INTR_SATA },
{ "hdaudio",
TEGRA_HDA_OFFSET, TEGRA_HDA_SIZE, NOPORT, TEGRA_INTR_HDA },
{ "tegracec",
TEGRA_CEC_OFFSET, TEGRA_CEC_SIZE, NOPORT, TEGRA_INTR_CEC },
};
static const struct tegra_locators tegra_ahb_a2_locators[] = {
{ "ehci",
TEGRA_USB1_OFFSET, TEGRA_USB1_SIZE, 0, TEGRA_INTR_USB1 },
{ "ehci",
TEGRA_USB2_OFFSET, TEGRA_USB2_SIZE, 1, TEGRA_INTR_USB2 },
{ "ehci",
TEGRA_USB3_OFFSET, TEGRA_USB3_SIZE, 2, TEGRA_INTR_USB3 },
};
static const struct tegra_locators tegra_pcie_locators[] = {
{ "tegrapcie",
TEGRA_PCIE_OFFSET, TEGRA_PCIE_SIZE, NOPORT, TEGRA_INTR_PCIE_INT },
};
static const struct tegra_locators tegra_host1x_locators[] = {
{ "tegrahost1x", 0, TEGRA_HOST1X_SIZE, NOPORT, NOINTR }
};
static const struct tegra_locators tegra_ghost_locators[] = {
{ "tegradc",
TEGRA_DISPLAYA_OFFSET, TEGRA_DISPLAYA_SIZE, 0, TEGRA_INTR_DISPLAYA },
{ "tegradc",
TEGRA_DISPLAYB_OFFSET, TEGRA_DISPLAYB_SIZE, 1, TEGRA_INTR_DISPLAYB },
{ "tegrahdmi",
TEGRA_HDMI_OFFSET, TEGRA_HDMI_SIZE, NOPORT, TEGRA_INTR_HDMI },
};
int
tegraio_match(device_t parent, cfdata_t cf, void *aux)
{
if (tegraio_found)
return 0;
return 1;
}
void
tegraio_attach(device_t parent, device_t self, void *aux)
{
tegraio_found = true;
aprint_naive("\n");
aprint_normal(": %s\n", tegra_chip_name());
tegraio_scan(self, tegra_ppsb_bsh,
tegra_ppsb_locators, __arraycount(tegra_ppsb_locators));
tegraio_scan(self, tegra_apb_bsh,
tegra_apb_locators, __arraycount(tegra_apb_locators));
tegraio_scan(self, tegra_ahb_a2_bsh,
tegra_ahb_a2_locators, __arraycount(tegra_ahb_a2_locators));
tegraio_scan(self, (bus_space_handle_t)NULL,
tegra_host1x_locators, __arraycount(tegra_host1x_locators));
tegraio_scan(self, (bus_space_handle_t)NULL,
tegra_ghost_locators, __arraycount(tegra_ghost_locators));
tegraio_scan(self, (bus_space_handle_t)NULL,
tegra_pcie_locators, __arraycount(tegra_pcie_locators));
}
static void
tegraio_scan(device_t self, bus_space_handle_t bsh,
const struct tegra_locators *locators, u_int count)
{
const struct tegra_locators * const eloc = locators + count;
for (const struct tegra_locators *loc = locators; loc < eloc; loc++) {
struct tegraio_attach_args tio = {
.tio_loc = *loc,
.tio_bst = &armv7_generic_bs_tag,
.tio_a4x_bst = &armv7_generic_a4x_bs_tag,
.tio_bsh = bsh,
.tio_dmat = &tegra_dma_tag,
.tio_coherent_dmat = &tegra_coherent_dma_tag,
};
cfdata_t cf = config_search_ia(tegraio_find, self,
"tegraio", &tio);
if (cf != NULL)
config_attach(self, cf, &tio, tegraio_print);
}
}
int
tegraio_print(void *aux, const char *pnp)
{
const struct tegraio_attach_args * const tio = aux;
if (tio->tio_loc.loc_port != TEGRAIOCF_PORT_DEFAULT)
aprint_normal(" port %d", tio->tio_loc.loc_port);
return UNCONF;
}
static int
tegraio_find(device_t parent, cfdata_t cf, const int *ldesc, void *aux)
{
const struct tegraio_attach_args * const tio = aux;
const struct tegra_locators * const loc = &tio->tio_loc;
const int port = cf->cf_loc[TEGRAIOCF_PORT];
if (strcmp(cf->cf_name, loc->loc_name)
|| (port != TEGRAIOCF_PORT_DEFAULT && port != loc->loc_port))
return 0;
return config_match(parent, cf, aux);
}