Index: head/sys/arm/freescale/vybrid/vf_ccm.c
===================================================================
--- head/sys/arm/freescale/vybrid/vf_ccm.c (revision 331923)
+++ head/sys/arm/freescale/vybrid/vf_ccm.c (revision 331924)
@@ -1,502 +1,502 @@
/*-
* SPDX-License-Identifier: BSD-2-Clause-FreeBSD
*
* Copyright (c) 2013-2014 Ruslan Bukin
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
/*
* Vybrid Family Clock Controller Module (CCM)
* Chapter 10, Vybrid Reference Manual, Rev. 5, 07/2013
*/
#include
__FBSDID("$FreeBSD$");
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define CCM_CCR 0x00 /* Control Register */
#define CCM_CSR 0x04 /* Status Register */
#define CCM_CCSR 0x08 /* Clock Switcher Register */
#define CCM_CACRR 0x0C /* ARM Clock Root Register */
#define CCM_CSCMR1 0x10 /* Serial Clock Multiplexer Register 1 */
#define CCM_CSCDR1 0x14 /* Serial Clock Divider Register 1 */
#define CCM_CSCDR2 0x18 /* Serial Clock Divider Register 2 */
#define CCM_CSCDR3 0x1C /* Serial Clock Divider Register 3 */
#define CCM_CSCMR2 0x20 /* Serial Clock Multiplexer Register 2 */
#define CCM_CTOR 0x28 /* Testing Observability Register */
#define CCM_CLPCR 0x2C /* Low Power Control Register */
#define CCM_CISR 0x30 /* Interrupt Status Register */
#define CCM_CIMR 0x34 /* Interrupt Mask Register */
#define CCM_CCOSR 0x38 /* Clock Output Source Register */
#define CCM_CGPR 0x3C /* General Purpose Register */
#define CCM_CCGRN 12
#define CCM_CCGR(n) (0x40 + (n * 0x04)) /* Clock Gating Register */
#define CCM_CMEOR(n) (0x70 + (n * 0x70)) /* Module Enable Override */
#define CCM_CCPGR(n) (0x90 + (n * 0x04)) /* Platform Clock Gating */
#define CCM_CPPDSR 0x88 /* PLL PFD Disable Status Register */
#define CCM_CCOWR 0x8C /* CORE Wakeup Register */
#define PLL3_PFD4_EN (1U << 31)
#define PLL3_PFD3_EN (1 << 30)
#define PLL3_PFD2_EN (1 << 29)
#define PLL3_PFD1_EN (1 << 28)
#define PLL2_PFD4_EN (1 << 15)
#define PLL2_PFD3_EN (1 << 14)
#define PLL2_PFD2_EN (1 << 13)
#define PLL2_PFD1_EN (1 << 12)
#define PLL1_PFD4_EN (1 << 11)
#define PLL1_PFD3_EN (1 << 10)
#define PLL1_PFD2_EN (1 << 9)
#define PLL1_PFD1_EN (1 << 8)
/* CCM_CCR */
#define FIRC_EN (1 << 16)
#define FXOSC_EN (1 << 12)
#define FXOSC_RDY (1 << 5)
/* CCM_CSCDR1 */
#define ENET_TS_EN (1 << 23)
#define RMII_CLK_EN (1 << 24)
#define SAI3_EN (1 << 19)
/* CCM_CSCDR2 */
#define ESAI_EN (1 << 30)
#define ESDHC1_EN (1 << 29)
#define ESDHC0_EN (1 << 28)
#define NFC_EN (1 << 9)
#define ESDHC1_DIV_S 20
#define ESDHC1_DIV_M 0xf
#define ESDHC0_DIV_S 16
#define ESDHC0_DIV_M 0xf
/* CCM_CSCDR3 */
#define DCU0_EN (1 << 19)
#define QSPI1_EN (1 << 12)
#define QSPI1_DIV (1 << 11)
#define QSPI1_X2_DIV (1 << 10)
#define QSPI1_X4_DIV_M 0x3
#define QSPI1_X4_DIV_S 8
#define QSPI0_EN (1 << 4)
#define QSPI0_DIV (1 << 3)
#define QSPI0_X2_DIV (1 << 2)
#define QSPI0_X4_DIV_M 0x3
#define QSPI0_X4_DIV_S 0
#define SAI3_DIV_SHIFT 12
#define SAI3_DIV_MASK 0xf
#define ESAI_DIV_SHIFT 24
#define ESAI_DIV_MASK 0xf
#define PLL4_CLK_DIV_SHIFT 6
#define PLL4_CLK_DIV_MASK 0x7
#define IPG_CLK_DIV_SHIFT 11
#define IPG_CLK_DIV_MASK 0x3
#define ESAI_CLK_SEL_SHIFT 20
#define ESAI_CLK_SEL_MASK 0x3
#define SAI3_CLK_SEL_SHIFT 6
#define SAI3_CLK_SEL_MASK 0x3
#define CKO1_EN (1 << 10)
#define CKO1_DIV_MASK 0xf
#define CKO1_DIV_SHIFT 6
#define CKO1_SEL_MASK 0x3f
#define CKO1_SEL_SHIFT 0
#define CKO1_PLL4_MAIN 0x6
#define CKO1_PLL4_DIVD 0x7
struct clk {
uint32_t reg;
uint32_t enable_reg;
uint32_t div_mask;
uint32_t div_shift;
uint32_t div_val;
uint32_t sel_reg;
uint32_t sel_mask;
uint32_t sel_shift;
uint32_t sel_val;
};
static struct clk ipg_clk = {
.reg = CCM_CACRR,
.enable_reg = 0,
.div_mask = IPG_CLK_DIV_MASK,
.div_shift = IPG_CLK_DIV_SHIFT,
.div_val = 1, /* Divide by 2 */
.sel_reg = 0,
.sel_mask = 0,
.sel_shift = 0,
.sel_val = 0,
};
/*
PLL4 clock divider (before switching the clocks should be gated)
000 Divide by 1 (only if PLL frequency less than or equal to 650 MHz)
001 Divide by 4
010 Divide by 6
011 Divide by 8
100 Divide by 10
101 Divide by 12
110 Divide by 14
111 Divide by 16
*/
static struct clk pll4_clk = {
.reg = CCM_CACRR,
.enable_reg = 0,
.div_mask = PLL4_CLK_DIV_MASK,
.div_shift = PLL4_CLK_DIV_SHIFT,
.div_val = 5, /* Divide by 12 */
.sel_reg = 0,
.sel_mask = 0,
.sel_shift = 0,
.sel_val = 0,
};
static struct clk sai3_clk = {
.reg = CCM_CSCDR1,
.enable_reg = SAI3_EN,
.div_mask = SAI3_DIV_MASK,
.div_shift = SAI3_DIV_SHIFT,
.div_val = 1,
.sel_reg = CCM_CSCMR1,
.sel_mask = SAI3_CLK_SEL_MASK,
.sel_shift = SAI3_CLK_SEL_SHIFT,
.sel_val = 0x3, /* Divided PLL4 main clock */
};
static struct clk cko1_clk = {
.reg = CCM_CCOSR,
.enable_reg = CKO1_EN,
.div_mask = CKO1_DIV_MASK,
.div_shift = CKO1_DIV_SHIFT,
.div_val = 1,
.sel_reg = CCM_CCOSR,
.sel_mask = CKO1_SEL_MASK,
.sel_shift = CKO1_SEL_SHIFT,
.sel_val = CKO1_PLL4_DIVD,
};
static struct clk esdhc0_clk = {
.reg = CCM_CSCDR2,
.enable_reg = ESDHC0_EN,
.div_mask = ESDHC0_DIV_M,
.div_shift = ESDHC0_DIV_S,
.div_val = 0x9,
.sel_reg = 0,
.sel_mask = 0,
.sel_shift = 0,
.sel_val = 0,
};
static struct clk esdhc1_clk = {
.reg = CCM_CSCDR2,
.enable_reg = ESDHC1_EN,
.div_mask = ESDHC1_DIV_M,
.div_shift = ESDHC1_DIV_S,
.div_val = 0x9,
.sel_reg = 0,
.sel_mask = 0,
.sel_shift = 0,
.sel_val = 0,
};
static struct clk qspi0_clk = {
.reg = CCM_CSCDR3,
.enable_reg = QSPI0_EN,
.div_mask = 0,
.div_shift = 0,
.div_val = 0,
.sel_reg = 0,
.sel_mask = 0,
.sel_shift = 0,
.sel_val = 0,
};
static struct clk dcu0_clk = {
.reg = CCM_CSCDR3,
.enable_reg = DCU0_EN,
.div_mask = 0x7,
.div_shift = 16, /* DCU0_DIV */
.div_val = 0, /* divide by 1 */
.sel_reg = 0,
.sel_mask = 0,
.sel_shift = 0,
.sel_val = 0,
};
static struct clk enet_clk = {
.reg = CCM_CSCDR1,
.enable_reg = (ENET_TS_EN | RMII_CLK_EN),
.div_mask = 0,
.div_shift = 0,
.div_val = 0,
.sel_reg = 0,
.sel_mask = 0,
.sel_shift = 0,
.sel_val = 0,
};
static struct clk nand_clk = {
.reg = CCM_CSCDR2,
.enable_reg = NFC_EN,
.div_mask = 0,
.div_shift = 0,
.div_val = 0,
.sel_reg = 0,
.sel_mask = 0,
.sel_shift = 0,
.sel_val = 0,
};
/*
Divider to generate ESAI clock
0000 Divide by 1
0001 Divide by 2
... ...
1111 Divide by 16
*/
static struct clk esai_clk = {
.reg = CCM_CSCDR2,
.enable_reg = ESAI_EN,
.div_mask = ESAI_DIV_MASK,
.div_shift = ESAI_DIV_SHIFT,
.div_val = 3, /* Divide by 4 */
.sel_reg = CCM_CSCMR1,
.sel_mask = ESAI_CLK_SEL_MASK,
.sel_shift = ESAI_CLK_SEL_SHIFT,
.sel_val = 0x3, /* Divided PLL4 main clock */
};
struct clock_entry {
char *name;
struct clk *clk;
};
static struct clock_entry clock_map[] = {
{"ipg", &ipg_clk},
{"pll4", &pll4_clk},
{"sai3", &sai3_clk},
{"cko1", &cko1_clk},
{"esdhc0", &esdhc0_clk},
{"esdhc1", &esdhc1_clk},
{"qspi0", &qspi0_clk},
{"dcu0", &dcu0_clk},
{"enet", &enet_clk},
{"nand", &nand_clk},
{"esai", &esai_clk},
{NULL, NULL}
};
struct ccm_softc {
struct resource *res[1];
bus_space_tag_t bst;
bus_space_handle_t bsh;
device_t dev;
};
static struct resource_spec ccm_spec[] = {
{ SYS_RES_MEMORY, 0, RF_ACTIVE },
{ -1, 0 }
};
static int
ccm_probe(device_t dev)
{
if (!ofw_bus_status_okay(dev))
return (ENXIO);
if (!ofw_bus_is_compatible(dev, "fsl,mvf600-ccm"))
return (ENXIO);
device_set_desc(dev, "Vybrid Family CCM Unit");
return (BUS_PROBE_DEFAULT);
}
static int
set_clock(struct ccm_softc *sc, char *name)
{
struct clk *clk;
int reg;
int i;
for (i = 0; clock_map[i].name != NULL; i++) {
if (strcmp(clock_map[i].name, name) == 0) {
#if 0
device_printf(sc->dev, "Configuring %s clk\n", name);
#endif
clk = clock_map[i].clk;
if (clk->sel_reg != 0) {
reg = READ4(sc, clk->sel_reg);
reg &= ~(clk->sel_mask << clk->sel_shift);
reg |= (clk->sel_val << clk->sel_shift);
WRITE4(sc, clk->sel_reg, reg);
}
reg = READ4(sc, clk->reg);
reg |= clk->enable_reg;
reg &= ~(clk->div_mask << clk->div_shift);
reg |= (clk->div_val << clk->div_shift);
WRITE4(sc, clk->reg, reg);
}
}
return (0);
}
static int
ccm_fdt_set(struct ccm_softc *sc)
{
phandle_t child, parent, root;
int len;
char *fdt_config, *name;
root = OF_finddevice("/");
len = 0;
parent = root;
/* Find 'clock_names' prop in the tree */
for (child = OF_child(parent); child != 0; child = OF_peer(child)) {
/* Find a 'leaf'. Start the search from this node. */
while (OF_child(child)) {
parent = child;
child = OF_child(child);
}
- if (!fdt_is_enabled(child))
+ if (!ofw_bus_node_status_okay(child))
continue;
if ((len = OF_getproplen(child, "clock_names")) > 0) {
len = OF_getproplen(child, "clock_names");
OF_getprop_alloc(child, "clock_names", 1,
(void **)&fdt_config);
while (len > 0) {
name = fdt_config;
fdt_config += strlen(name) + 1;
len -= strlen(name) + 1;
set_clock(sc, name);
}
}
if (OF_peer(child) == 0) {
/* No more siblings. */
child = parent;
parent = OF_parent(child);
}
}
return (0);
}
static int
ccm_attach(device_t dev)
{
struct ccm_softc *sc;
int reg;
int i;
sc = device_get_softc(dev);
sc->dev = dev;
if (bus_alloc_resources(dev, ccm_spec, sc->res)) {
device_printf(dev, "could not allocate resources\n");
return (ENXIO);
}
/* Memory interface */
sc->bst = rman_get_bustag(sc->res[0]);
sc->bsh = rman_get_bushandle(sc->res[0]);
/* Enable oscillator */
reg = READ4(sc, CCM_CCR);
reg |= (FIRC_EN | FXOSC_EN);
WRITE4(sc, CCM_CCR, reg);
/* Wait 10 times */
for (i = 0; i < 10; i++) {
if (READ4(sc, CCM_CSR) & FXOSC_RDY) {
device_printf(sc->dev, "On board oscillator is ready.\n");
break;
}
cpufunc_nullop();
}
/* Clock is on during all modes, except stop mode. */
for (i = 0; i < CCM_CCGRN; i++) {
WRITE4(sc, CCM_CCGR(i), 0xffffffff);
}
/* Take and apply FDT clocks */
ccm_fdt_set(sc);
return (0);
}
static device_method_t ccm_methods[] = {
DEVMETHOD(device_probe, ccm_probe),
DEVMETHOD(device_attach, ccm_attach),
{ 0, 0 }
};
static driver_t ccm_driver = {
"ccm",
ccm_methods,
sizeof(struct ccm_softc),
};
static devclass_t ccm_devclass;
DRIVER_MODULE(ccm, simplebus, ccm_driver, ccm_devclass, 0, 0);
Index: head/sys/arm/freescale/vybrid/vf_iomuxc.c
===================================================================
--- head/sys/arm/freescale/vybrid/vf_iomuxc.c (revision 331923)
+++ head/sys/arm/freescale/vybrid/vf_iomuxc.c (revision 331924)
@@ -1,213 +1,213 @@
/*-
* SPDX-License-Identifier: BSD-2-Clause-FreeBSD
*
* Copyright (c) 2013-2014 Ruslan Bukin
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
/*
* Vybrid Family Input/Output Multiplexer Controller (IOMUXC)
* Chapter 5, Vybrid Reference Manual, Rev. 5, 07/2013
*/
#include
__FBSDID("$FreeBSD$");
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define MUX_MODE_MASK 7
#define MUX_MODE_SHIFT 20
#define MUX_MODE_GPIO 0
#define MUX_MODE_VBUS_EN_OTG 2
#define IBE (1 << 0) /* Input Buffer Enable Field */
#define OBE (1 << 1) /* Output Buffer Enable Field. */
#define PUE (1 << 2) /* Pull / Keep Select Field. */
#define PKE (1 << 3) /* Pull / Keep Enable Field. */
#define HYS (1 << 9) /* Hysteresis Enable Field */
#define ODE (1 << 10) /* Open Drain Enable Field. */
#define SRE (1 << 11) /* Slew Rate Field. */
#define SPEED_SHIFT 12
#define SPEED_MASK 0x3
#define SPEED_LOW 0 /* 50 MHz */
#define SPEED_MEDIUM 0x1 /* 100 MHz */
#define SPEED_HIGH 0x3 /* 200 MHz */
#define PUS_SHIFT 4 /* Pull Up / Down Config Field Shift */
#define PUS_MASK 0x3
#define PUS_100_KOHM_PULL_DOWN 0
#define PUS_47_KOHM_PULL_UP 0x1
#define PUS_100_KOHM_PULL_UP 0x2
#define PUS_22_KOHM_PULL_UP 0x3
#define DSE_SHIFT 6 /* Drive Strength Field Shift */
#define DSE_MASK 0x7
#define DSE_DISABLED 0 /* Output driver disabled */
#define DSE_150_OHM 0x1
#define DSE_75_OHM 0x2
#define DSE_50_OHM 0x3
#define DSE_37_OHM 0x4
#define DSE_30_OHM 0x5
#define DSE_25_OHM 0x6
#define DSE_20_OHM 0x7
#define MAX_MUX_LEN 1024
struct iomuxc_softc {
struct resource *tmr_res[1];
bus_space_tag_t bst;
bus_space_handle_t bsh;
device_t dev;
};
static struct resource_spec iomuxc_spec[] = {
{ SYS_RES_MEMORY, 0, RF_ACTIVE },
{ -1, 0 }
};
static int
iomuxc_probe(device_t dev)
{
if (!ofw_bus_status_okay(dev))
return (ENXIO);
if (!ofw_bus_is_compatible(dev, "fsl,mvf600-iomuxc"))
return (ENXIO);
device_set_desc(dev, "Vybrid Family IOMUXC Unit");
return (BUS_PROBE_DEFAULT);
}
static int
pinmux_set(struct iomuxc_softc *sc)
{
phandle_t child, parent, root;
pcell_t iomux_config[MAX_MUX_LEN];
int len;
int values;
int pin;
int pin_cfg;
int i;
root = OF_finddevice("/");
len = 0;
parent = root;
/* Find 'iomux_config' prop in the nodes */
for (child = OF_child(parent); child != 0; child = OF_peer(child)) {
/* Find a 'leaf'. Start the search from this node. */
while (OF_child(child)) {
parent = child;
child = OF_child(child);
}
- if (!fdt_is_enabled(child))
+ if (!ofw_bus_node_status_okay(child))
continue;
if ((len = OF_getproplen(child, "iomux_config")) > 0) {
OF_getencprop(child, "iomux_config", iomux_config, len);
values = len / (sizeof(uint32_t));
for (i = 0; i < values; i += 2) {
pin = iomux_config[i];
pin_cfg = iomux_config[i+1];
#if 0
device_printf(sc->dev, "Set pin %d to 0x%08x\n",
pin, pin_cfg);
#endif
WRITE4(sc, IOMUXC(pin), pin_cfg);
}
}
if (OF_peer(child) == 0) {
/* No more siblings. */
child = parent;
parent = OF_parent(child);
}
}
return (0);
}
static int
iomuxc_attach(device_t dev)
{
struct iomuxc_softc *sc;
sc = device_get_softc(dev);
sc->dev = dev;
if (bus_alloc_resources(dev, iomuxc_spec, sc->tmr_res)) {
device_printf(dev, "could not allocate resources\n");
return (ENXIO);
}
/* Memory interface */
sc->bst = rman_get_bustag(sc->tmr_res[0]);
sc->bsh = rman_get_bushandle(sc->tmr_res[0]);
pinmux_set(sc);
return (0);
}
static device_method_t iomuxc_methods[] = {
DEVMETHOD(device_probe, iomuxc_probe),
DEVMETHOD(device_attach, iomuxc_attach),
{ 0, 0 }
};
static driver_t iomuxc_driver = {
"iomuxc",
iomuxc_methods,
sizeof(struct iomuxc_softc),
};
static devclass_t iomuxc_devclass;
DRIVER_MODULE(iomuxc, simplebus, iomuxc_driver, iomuxc_devclass, 0, 0);
Index: head/sys/arm/mv/mv_localbus.c
===================================================================
--- head/sys/arm/mv/mv_localbus.c (revision 331923)
+++ head/sys/arm/mv/mv_localbus.c (revision 331924)
@@ -1,494 +1,494 @@
/*-
* SPDX-License-Identifier: BSD-2-Clause-FreeBSD
*
* Copyright (c) 2012 Semihalf.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include
__FBSDID("$FreeBSD$");
#include "opt_platform.h"
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "dev/fdt/fdt_common.h"
#include "ofw_bus_if.h"
#include
#include
#ifdef DEBUG
#define debugf(fmt, args...) do { printf("%s(): ", __func__); \
printf(fmt,##args); } while (0)
#else
#define debugf(fmt, args...)
#endif
#define MV_LOCALBUS_MAX_BANKS 8
#define MV_LOCALBUS_MAX_BANK_CELLS 4
static MALLOC_DEFINE(M_LOCALBUS, "localbus", "localbus devices information");
struct localbus_bank {
vm_offset_t va; /* VA of the bank */
vm_paddr_t pa; /* physical address of the bank */
vm_size_t size; /* bank size */
uint8_t mapped; /* device memory has mapping */
};
struct localbus_softc {
device_t sc_dev;
bus_space_handle_t sc_bsh;
bus_space_tag_t sc_bst;
int sc_rid;
struct localbus_bank *sc_banks;
};
struct localbus_devinfo {
struct ofw_bus_devinfo di_ofw;
struct resource_list di_res;
int di_bank;
};
struct localbus_va_entry {
int8_t bank;
vm_offset_t va;
vm_size_t size;
};
/*
* Prototypes.
*/
static int localbus_probe(device_t);
static int localbus_attach(device_t);
static int localbus_print_child(device_t, device_t);
static struct resource *localbus_alloc_resource(device_t, device_t, int,
int *, rman_res_t, rman_res_t, rman_res_t, u_int);
static struct resource_list *localbus_get_resource_list(device_t, device_t);
static ofw_bus_get_devinfo_t localbus_get_devinfo;
/*
* Bus interface definition.
*/
static device_method_t localbus_methods[] = {
/* Device interface */
DEVMETHOD(device_probe, localbus_probe),
DEVMETHOD(device_attach, localbus_attach),
DEVMETHOD(device_detach, bus_generic_detach),
DEVMETHOD(device_shutdown, bus_generic_shutdown),
DEVMETHOD(device_suspend, bus_generic_suspend),
DEVMETHOD(device_resume, bus_generic_resume),
/* Bus interface */
DEVMETHOD(bus_print_child, localbus_print_child),
DEVMETHOD(bus_alloc_resource, localbus_alloc_resource),
DEVMETHOD(bus_release_resource, bus_generic_release_resource),
DEVMETHOD(bus_activate_resource, bus_generic_activate_resource),
DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource),
DEVMETHOD(bus_setup_intr, bus_generic_setup_intr),
DEVMETHOD(bus_teardown_intr, bus_generic_teardown_intr),
DEVMETHOD(bus_get_resource_list, localbus_get_resource_list),
/* OFW bus interface */
DEVMETHOD(ofw_bus_get_devinfo, localbus_get_devinfo),
DEVMETHOD(ofw_bus_get_compat, ofw_bus_gen_get_compat),
DEVMETHOD(ofw_bus_get_model, ofw_bus_gen_get_model),
DEVMETHOD(ofw_bus_get_name, ofw_bus_gen_get_name),
DEVMETHOD(ofw_bus_get_node, ofw_bus_gen_get_node),
DEVMETHOD(ofw_bus_get_type, ofw_bus_gen_get_type),
{ 0, 0 }
};
static driver_t localbus_driver = {
"localbus",
localbus_methods,
sizeof(struct localbus_softc)
};
const struct localbus_va_entry localbus_virtmap[] = {
{ 0, MV_DEV_BOOT_BASE, MV_DEV_BOOT_SIZE },
{ 1, MV_DEV_CS0_BASE, MV_DEV_CS0_SIZE },
{ 2, MV_DEV_CS1_BASE, MV_DEV_CS1_SIZE },
{ 3, MV_DEV_CS2_BASE, MV_DEV_CS2_SIZE },
{ -1, 0, 0 }
};
static struct localbus_bank localbus_banks[MV_LOCALBUS_MAX_BANKS];
devclass_t localbus_devclass;
DRIVER_MODULE(localbus, ofwbus, localbus_driver, localbus_devclass, 0, 0);
static int
fdt_localbus_reg_decode(phandle_t node, struct localbus_softc *sc,
struct localbus_devinfo *di)
{
u_long start, end, count;
pcell_t *reg, *regptr;
pcell_t addr_cells, size_cells;
int tuple_size, tuples;
int i, rv, bank;
if (fdt_addrsize_cells(OF_parent(node), &addr_cells, &size_cells) != 0)
return (ENXIO);
tuple_size = sizeof(pcell_t) * (addr_cells + size_cells);
tuples = OF_getprop_alloc(node, "reg", tuple_size, (void **)®);
debugf("addr_cells = %d, size_cells = %d\n", addr_cells, size_cells);
debugf("tuples = %d, tuple size = %d\n", tuples, tuple_size);
if (tuples <= 0)
/* No 'reg' property in this node. */
return (0);
regptr = reg;
for (i = 0; i < tuples; i++) {
bank = fdt_data_get((void *)regptr, 1);
if (bank >= MV_LOCALBUS_MAX_BANKS) {
device_printf(sc->sc_dev, "bank number [%d] out of "
"range\n", bank);
continue;
}
/*
* If device doesn't have virtual to physical mapping don't add
* resources
*/
if (!(sc->sc_banks[bank].mapped)) {
device_printf(sc->sc_dev, "device [%d]: missing memory "
"mapping\n", bank);
continue;
}
di->di_bank = bank;
regptr += 1;
/* Get address/size. */
rv = fdt_data_to_res(regptr, addr_cells - 1, size_cells, &start,
&count);
if (rv != 0) {
resource_list_free(&di->di_res);
goto out;
}
/* Check if enough amount of memory is mapped */
if (sc->sc_banks[bank].size < count) {
device_printf(sc->sc_dev, "device [%d]: not enough "
"memory reserved\n", bank);
continue;
}
regptr += addr_cells - 1 + size_cells;
/* Calculate address range relative to VA base. */
start = sc->sc_banks[bank].va + start;
end = start + count - 1;
debugf("reg addr bank = %d, start = %lx, end = %lx, "
"count = %lx\n", bank, start, end, count);
/* Use bank (CS) cell as rid. */
resource_list_add(&di->di_res, SYS_RES_MEMORY, di->di_bank,
start, end, count);
}
rv = 0;
out:
OF_prop_free(reg);
return (rv);
}
static int
localbus_probe(device_t dev)
{
if (!ofw_bus_is_compatible_strict(dev, "mrvl,lbc"))
return (ENXIO);
device_set_desc(dev, "Marvell device bus");
return (BUS_PROBE_DEFAULT);
}
static int
localbus_attach(device_t dev)
{
device_t dev_child;
struct localbus_softc *sc;
struct localbus_devinfo *di;
phandle_t dt_node, dt_child;
sc = device_get_softc(dev);
sc->sc_dev = dev;
sc->sc_banks = localbus_banks;
/*
* Walk localbus and add direct subordinates as our children.
*/
dt_node = ofw_bus_get_node(dev);
for (dt_child = OF_child(dt_node); dt_child != 0;
dt_child = OF_peer(dt_child)) {
/* Check and process 'status' property. */
- if (!(fdt_is_enabled(dt_child)))
+ if (!(ofw_bus_node_status_okay(dt_child)))
continue;
if (!(fdt_pm_is_enabled(dt_child)))
continue;
di = malloc(sizeof(*di), M_LOCALBUS, M_WAITOK | M_ZERO);
if (ofw_bus_gen_setup_devinfo(&di->di_ofw, dt_child) != 0) {
free(di, M_LOCALBUS);
device_printf(dev, "could not set up devinfo\n");
continue;
}
resource_list_init(&di->di_res);
if (fdt_localbus_reg_decode(dt_child, sc, di)) {
device_printf(dev, "could not process 'reg' "
"property\n");
ofw_bus_gen_destroy_devinfo(&di->di_ofw);
free(di, M_LOCALBUS);
continue;
}
/* Add newbus device for this FDT node */
dev_child = device_add_child(dev, NULL, -1);
if (dev_child == NULL) {
device_printf(dev, "could not add child: %s\n",
di->di_ofw.obd_name);
resource_list_free(&di->di_res);
ofw_bus_gen_destroy_devinfo(&di->di_ofw);
free(di, M_LOCALBUS);
continue;
}
#ifdef DEBUG
device_printf(dev, "added child: %s\n\n", di->di_ofw.obd_name);
#endif
device_set_ivars(dev_child, di);
}
return (bus_generic_attach(dev));
}
static int
localbus_print_child(device_t dev, device_t child)
{
struct localbus_devinfo *di;
struct resource_list *rl;
int rv;
di = device_get_ivars(child);
rl = &di->di_res;
rv = 0;
rv += bus_print_child_header(dev, child);
rv += resource_list_print_type(rl, "mem", SYS_RES_MEMORY, "%#jx");
rv += resource_list_print_type(rl, "irq", SYS_RES_IRQ, "%jd");
rv += bus_print_child_footer(dev, child);
return (rv);
}
static struct resource *
localbus_alloc_resource(device_t bus, device_t child, int type, int *rid,
rman_res_t start, rman_res_t end, rman_res_t count, u_int flags)
{
struct localbus_devinfo *di;
struct resource_list_entry *rle;
/*
* Request for the default allocation with a given rid: use resource
* list stored in the local device info.
*/
if (RMAN_IS_DEFAULT_RANGE(start, end)) {
if ((di = device_get_ivars(child)) == NULL)
return (NULL);
if (type == SYS_RES_IOPORT)
type = SYS_RES_MEMORY;
rid = &di->di_bank;
rle = resource_list_find(&di->di_res, type, *rid);
if (rle == NULL) {
device_printf(bus, "no default resources for "
"rid = %d, type = %d\n", *rid, type);
return (NULL);
}
start = rle->start;
end = rle->end;
count = rle->count;
}
return (bus_generic_alloc_resource(bus, child, type, rid, start, end,
count, flags));
}
static struct resource_list *
localbus_get_resource_list(device_t bus, device_t child)
{
struct localbus_devinfo *di;
di = device_get_ivars(child);
return (&di->di_res);
}
static const struct ofw_bus_devinfo *
localbus_get_devinfo(device_t bus, device_t child)
{
struct localbus_devinfo *di;
di = device_get_ivars(child);
return (&di->di_ofw);
}
int
fdt_localbus_devmap(phandle_t dt_node, struct devmap_entry *fdt_devmap,
int banks_max_num, int *banks_added)
{
pcell_t ranges[MV_LOCALBUS_MAX_BANKS * MV_LOCALBUS_MAX_BANK_CELLS];
pcell_t *rangesptr;
uint32_t tuple_size, bank;
vm_paddr_t offset;
vm_size_t size;
int dev_num, addr_cells, size_cells, par_addr_cells, va_index, i, j, k;
if ((fdt_addrsize_cells(dt_node, &addr_cells, &size_cells)) != 0)
return (EINVAL);
par_addr_cells = fdt_parent_addr_cells(dt_node);
if (par_addr_cells > 2) {
/*
* Localbus devmap initialization error: unsupported parent
* #addr-cells
*/
return (ERANGE);
}
tuple_size = (addr_cells + par_addr_cells + size_cells);
if (tuple_size > MV_LOCALBUS_MAX_BANK_CELLS)
return (ERANGE);
tuple_size *= sizeof(pcell_t);
dev_num = OF_getprop(dt_node, "ranges", ranges, sizeof(ranges));
if (dev_num <= 0)
return (EINVAL);
/* Calculate number of devices attached to bus */
dev_num = dev_num / tuple_size;
/*
* If number of ranges > max number of localbus devices,
* additional entries will not be processed
*/
dev_num = MIN(dev_num, banks_max_num);
rangesptr = &ranges[0];
j = 0;
/* Process data from FDT */
for (i = 0; i < dev_num; i++) {
/* First field is bank number */
bank = fdt_data_get((void *)rangesptr, 1);
rangesptr += 1;
if (bank > MV_LOCALBUS_MAX_BANKS) {
/* Bank out of range */
rangesptr += ((addr_cells - 1) + par_addr_cells +
size_cells);
continue;
}
/* Find virtmap entry for this bank */
va_index = -1;
for (k = 0; localbus_virtmap[k].bank >= 0; k++) {
if (localbus_virtmap[k].bank == bank) {
va_index = k;
break;
}
}
/* Check if virtmap entry was found */
if (va_index == -1) {
rangesptr += ((addr_cells - 1) + par_addr_cells +
size_cells);
continue;
}
/* Remaining child's address fields are unused */
rangesptr += (addr_cells - 1);
/* Parent address offset */
offset = fdt_data_get((void *)rangesptr, par_addr_cells);
rangesptr += par_addr_cells;
/* Last field is size */
size = fdt_data_get((void *)rangesptr, size_cells);
rangesptr += size_cells;
if (size > localbus_virtmap[va_index].size) {
/* Not enough space reserved in virtual memory map */
continue;
}
fdt_devmap[j].pd_va = localbus_virtmap[va_index].va;
fdt_devmap[j].pd_pa = offset;
fdt_devmap[j].pd_size = size;
/* Copy data to structure used by localbus driver */
localbus_banks[bank].va = fdt_devmap[j].pd_va;
localbus_banks[bank].pa = fdt_devmap[j].pd_pa;
localbus_banks[bank].size = fdt_devmap[j].pd_size;
localbus_banks[bank].mapped = 1;
j++;
}
*banks_added = j;
return (0);
}
Index: head/sys/arm/nvidia/as3722_gpio.c
===================================================================
--- head/sys/arm/nvidia/as3722_gpio.c (revision 331923)
+++ head/sys/arm/nvidia/as3722_gpio.c (revision 331924)
@@ -1,577 +1,577 @@
/*-
* Copyright (c) 2016 Michal Meloun
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include
__FBSDID("$FreeBSD$");
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "as3722.h"
MALLOC_DEFINE(M_AS3722_GPIO, "AS3722 gpio", "AS3722 GPIO");
/* AS3722_GPIOx_CONTROL MODE and IOSF definition. */
#define AS3722_IOSF_GPIO 0x00
#define AS3722_IOSF_INTERRUPT_OUT 0x01
#define AS3722_IOSF_VSUP_VBAT_LOW_UNDEBOUNCE_OUT 0x02
#define AS3722_IOSF_GPIO_IN_INTERRUPT 0x03
#define AS3722_IOSF_PWM_IN 0x04
#define AS3722_IOSF_VOLTAGE_IN_STANDBY 0x05
#define AS3722_IOSF_OC_PG_SD0 0x06
#define AS3722_IOSF_POWERGOOD_OUT 0x07
#define AS3722_IOSF_CLK32K_OUT 0x08
#define AS3722_IOSF_WATCHDOG_IN 0x09
#define AS3722_IOSF_SOFT_RESET_IN 0x0b
#define AS3722_IOSF_PWM_OUT 0x0c
#define AS3722_IOSF_VSUP_VBAT_LOW_DEBOUNCE_OUT 0x0d
#define AS3722_IOSF_OC_PG_SD6 0x0e
#define AS3722_MODE_INPUT 0
#define AS3722_MODE_PUSH_PULL 1
#define AS3722_MODE_OPEN_DRAIN 2
#define AS3722_MODE_TRISTATE 3
#define AS3722_MODE_INPUT_PULL_UP_LV 4
#define AS3722_MODE_INPUT_PULL_DOWN 5
#define AS3722_MODE_OPEN_DRAIN_LV 6
#define AS3722_MODE_PUSH_PULL_LV 7
#define NGPIO 8
#define GPIO_LOCK(_sc) sx_slock(&(_sc)->gpio_lock)
#define GPIO_UNLOCK(_sc) sx_unlock(&(_sc)->gpio_lock)
#define GPIO_ASSERT(_sc) sx_assert(&(_sc)->gpio_lock, SA_LOCKED)
#define AS3722_CFG_BIAS_DISABLE 0x0001
#define AS3722_CFG_BIAS_PULL_UP 0x0002
#define AS3722_CFG_BIAS_PULL_DOWN 0x0004
#define AS3722_CFG_BIAS_HIGH_IMPEDANCE 0x0008
#define AS3722_CFG_OPEN_DRAIN 0x0010
static const struct {
const char *name;
int config; /* AS3722_CFG_ */
} as3722_cfg_names[] = {
{"bias-disable", AS3722_CFG_BIAS_DISABLE},
{"bias-pull-up", AS3722_CFG_BIAS_PULL_UP},
{"bias-pull-down", AS3722_CFG_BIAS_PULL_DOWN},
{"bias-high-impedance", AS3722_CFG_BIAS_HIGH_IMPEDANCE},
{"drive-open-drain", AS3722_CFG_OPEN_DRAIN},
};
static struct {
const char *name;
int fnc_val;
} as3722_fnc_table[] = {
{"gpio", AS3722_IOSF_GPIO},
{"interrupt-out", AS3722_IOSF_INTERRUPT_OUT},
{"vsup-vbat-low-undebounce-out", AS3722_IOSF_VSUP_VBAT_LOW_UNDEBOUNCE_OUT},
{"gpio-in-interrupt", AS3722_IOSF_GPIO_IN_INTERRUPT},
{"pwm-in", AS3722_IOSF_PWM_IN},
{"voltage-in-standby", AS3722_IOSF_VOLTAGE_IN_STANDBY},
{"oc-pg-sd0", AS3722_IOSF_OC_PG_SD0},
{"powergood-out", AS3722_IOSF_POWERGOOD_OUT},
{"clk32k-out", AS3722_IOSF_CLK32K_OUT},
{"watchdog-in", AS3722_IOSF_WATCHDOG_IN},
{"soft-reset-in", AS3722_IOSF_SOFT_RESET_IN},
{"pwm-out", AS3722_IOSF_PWM_OUT},
{"vsup-vbat-low-debounce-out", AS3722_IOSF_VSUP_VBAT_LOW_DEBOUNCE_OUT},
{"oc-pg-sd6", AS3722_IOSF_OC_PG_SD6},
};
struct as3722_pincfg {
char *function;
int flags;
};
struct as3722_gpio_pin {
int pin_caps;
uint8_t pin_ctrl_reg;
char pin_name[GPIOMAXNAME];
int pin_cfg_flags;
};
/* --------------------------------------------------------------------------
*
* Pinmux functions.
*/
static int
as3722_pinmux_get_function(struct as3722_softc *sc, char *name)
{
int i;
for (i = 0; i < nitems(as3722_fnc_table); i++) {
if (strcmp(as3722_fnc_table[i].name, name) == 0)
return (as3722_fnc_table[i].fnc_val);
}
return (-1);
}
static int
as3722_pinmux_config_node(struct as3722_softc *sc, char *pin_name,
struct as3722_pincfg *cfg)
{
uint8_t ctrl;
int rv, fnc, pin;
for (pin = 0; pin < sc->gpio_npins; pin++) {
if (strcmp(sc->gpio_pins[pin]->pin_name, pin_name) == 0)
break;
}
if (pin >= sc->gpio_npins) {
device_printf(sc->dev, "Unknown pin: %s\n", pin_name);
return (ENXIO);
}
ctrl = sc->gpio_pins[pin]->pin_ctrl_reg;
sc->gpio_pins[pin]->pin_cfg_flags = cfg->flags;
if (cfg->function != NULL) {
fnc = as3722_pinmux_get_function(sc, cfg->function);
if (fnc == -1) {
device_printf(sc->dev,
"Unknown function %s for pin %s\n", cfg->function,
sc->gpio_pins[pin]->pin_name);
return (ENXIO);
}
switch (fnc) {
case AS3722_IOSF_INTERRUPT_OUT:
case AS3722_IOSF_VSUP_VBAT_LOW_UNDEBOUNCE_OUT:
case AS3722_IOSF_OC_PG_SD0:
case AS3722_IOSF_POWERGOOD_OUT:
case AS3722_IOSF_CLK32K_OUT:
case AS3722_IOSF_PWM_OUT:
case AS3722_IOSF_OC_PG_SD6:
ctrl &= ~(AS3722_GPIO_MODE_MASK <<
AS3722_GPIO_MODE_SHIFT);
ctrl |= AS3722_MODE_PUSH_PULL << AS3722_GPIO_MODE_SHIFT;
/* XXX Handle flags (OC + pullup) */
break;
case AS3722_IOSF_GPIO_IN_INTERRUPT:
case AS3722_IOSF_PWM_IN:
case AS3722_IOSF_VOLTAGE_IN_STANDBY:
case AS3722_IOSF_WATCHDOG_IN:
case AS3722_IOSF_SOFT_RESET_IN:
ctrl &= ~(AS3722_GPIO_MODE_MASK <<
AS3722_GPIO_MODE_SHIFT);
ctrl |= AS3722_MODE_INPUT << AS3722_GPIO_MODE_SHIFT;
/* XXX Handle flags (pulldown + pullup) */
default:
break;
}
ctrl &= ~(AS3722_GPIO_IOSF_MASK << AS3722_GPIO_IOSF_SHIFT);
ctrl |= fnc << AS3722_GPIO_IOSF_SHIFT;
}
rv = 0;
if (ctrl != sc->gpio_pins[pin]->pin_ctrl_reg) {
rv = WR1(sc, AS3722_GPIO0_CONTROL + pin, ctrl);
sc->gpio_pins[pin]->pin_ctrl_reg = ctrl;
}
return (rv);
}
static int
as3722_pinmux_read_node(struct as3722_softc *sc, phandle_t node,
struct as3722_pincfg *cfg, char **pins, int *lpins)
{
int rv, i;
*lpins = OF_getprop_alloc(node, "pins", 1, (void **)pins);
if (*lpins <= 0)
return (ENOENT);
/* Read function (mux) settings. */
rv = OF_getprop_alloc(node, "function", 1, (void **)&cfg->function);
if (rv <= 0)
cfg->function = NULL;
/* Read boolean properties. */
for (i = 0; i < nitems(as3722_cfg_names); i++) {
if (OF_hasprop(node, as3722_cfg_names[i].name))
cfg->flags |= as3722_cfg_names[i].config;
}
return (0);
}
static int
as3722_pinmux_process_node(struct as3722_softc *sc, phandle_t node)
{
struct as3722_pincfg cfg;
char *pins, *pname;
int i, len, lpins, rv;
rv = as3722_pinmux_read_node(sc, node, &cfg, &pins, &lpins);
if (rv != 0)
return (rv);
len = 0;
pname = pins;
do {
i = strlen(pname) + 1;
rv = as3722_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);
}
int as3722_pinmux_configure(device_t dev, phandle_t cfgxref)
{
struct as3722_softc *sc;
phandle_t node, cfgnode;
int rv;
sc = device_get_softc(dev);
cfgnode = OF_node_from_xref(cfgxref);
for (node = OF_child(cfgnode); node != 0; node = OF_peer(node)) {
- if (!fdt_is_enabled(node))
+ if (!ofw_bus_node_status_okay(node))
continue;
rv = as3722_pinmux_process_node(sc, node);
if (rv != 0)
device_printf(dev, "Failed to process pinmux");
}
return (0);
}
/* --------------------------------------------------------------------------
*
* GPIO
*/
device_t
as3722_gpio_get_bus(device_t dev)
{
struct as3722_softc *sc;
sc = device_get_softc(dev);
return (sc->gpio_busdev);
}
int
as3722_gpio_pin_max(device_t dev, int *maxpin)
{
*maxpin = NGPIO - 1;
return (0);
}
int
as3722_gpio_pin_getcaps(device_t dev, uint32_t pin, uint32_t *caps)
{
struct as3722_softc *sc;
sc = device_get_softc(dev);
if (pin >= sc->gpio_npins)
return (EINVAL);
GPIO_LOCK(sc);
*caps = sc->gpio_pins[pin]->pin_caps;
GPIO_UNLOCK(sc);
return (0);
}
int
as3722_gpio_pin_getname(device_t dev, uint32_t pin, char *name)
{
struct as3722_softc *sc;
sc = device_get_softc(dev);
if (pin >= sc->gpio_npins)
return (EINVAL);
GPIO_LOCK(sc);
memcpy(name, sc->gpio_pins[pin]->pin_name, GPIOMAXNAME);
GPIO_UNLOCK(sc);
return (0);
}
int
as3722_gpio_pin_getflags(device_t dev, uint32_t pin, uint32_t *out_flags)
{
struct as3722_softc *sc;
uint8_t tmp, mode, iosf;
uint32_t flags;
bool inverted;
sc = device_get_softc(dev);
if (pin >= sc->gpio_npins)
return (EINVAL);
GPIO_LOCK(sc);
tmp = sc->gpio_pins[pin]->pin_ctrl_reg;
GPIO_UNLOCK(sc);
iosf = (tmp >> AS3722_GPIO_IOSF_SHIFT) & AS3722_GPIO_IOSF_MASK;
mode = (tmp >> AS3722_GPIO_MODE_SHIFT) & AS3722_GPIO_MODE_MASK;
inverted = (tmp & AS3722_GPIO_INVERT) != 0;
/* Is pin in GPIO mode ? */
if (iosf != AS3722_IOSF_GPIO)
return (ENXIO);
flags = 0;
switch (mode) {
case AS3722_MODE_INPUT:
flags = GPIO_PIN_INPUT;
break;
case AS3722_MODE_PUSH_PULL:
case AS3722_MODE_PUSH_PULL_LV:
flags = GPIO_PIN_OUTPUT;
break;
case AS3722_MODE_OPEN_DRAIN:
case AS3722_MODE_OPEN_DRAIN_LV:
flags = GPIO_PIN_INPUT | GPIO_PIN_OUTPUT | GPIO_PIN_OPENDRAIN;
break;
case AS3722_MODE_TRISTATE:
flags = GPIO_PIN_TRISTATE;
break;
case AS3722_MODE_INPUT_PULL_UP_LV:
flags = GPIO_PIN_INPUT | GPIO_PIN_PULLUP;
break;
case AS3722_MODE_INPUT_PULL_DOWN:
flags = GPIO_PIN_OUTPUT | GPIO_PIN_PULLDOWN;
break;
}
if (inverted)
flags |= GPIO_PIN_INVIN | GPIO_PIN_INVOUT;
*out_flags = flags;
return (0);
}
static int
as3722_gpio_get_mode(struct as3722_softc *sc, uint32_t pin, uint32_t gpio_flags)
{
uint8_t ctrl;
int flags;
ctrl = sc->gpio_pins[pin]->pin_ctrl_reg;
flags = sc->gpio_pins[pin]->pin_cfg_flags;
/* Tristate mode. */
if (flags & AS3722_CFG_BIAS_HIGH_IMPEDANCE ||
gpio_flags & GPIO_PIN_TRISTATE)
return (AS3722_MODE_TRISTATE);
/* Open drain modes. */
if (flags & AS3722_CFG_OPEN_DRAIN || gpio_flags & GPIO_PIN_OPENDRAIN) {
/* Only pull up have effect */
if (flags & AS3722_CFG_BIAS_PULL_UP ||
gpio_flags & GPIO_PIN_PULLUP)
return (AS3722_MODE_OPEN_DRAIN_LV);
return (AS3722_MODE_OPEN_DRAIN);
}
/* Input modes. */
if (gpio_flags & GPIO_PIN_INPUT) {
/* Accept pull up or pull down. */
if (flags & AS3722_CFG_BIAS_PULL_UP ||
gpio_flags & GPIO_PIN_PULLUP)
return (AS3722_MODE_INPUT_PULL_UP_LV);
if (flags & AS3722_CFG_BIAS_PULL_DOWN ||
gpio_flags & GPIO_PIN_PULLDOWN)
return (AS3722_MODE_INPUT_PULL_DOWN);
return (AS3722_MODE_INPUT);
}
/*
* Output modes.
* Pull down is used as indicator of low voltage output.
*/
if (flags & AS3722_CFG_BIAS_PULL_DOWN ||
gpio_flags & GPIO_PIN_PULLDOWN)
return (AS3722_MODE_PUSH_PULL_LV);
return (AS3722_MODE_PUSH_PULL);
}
int
as3722_gpio_pin_setflags(device_t dev, uint32_t pin, uint32_t flags)
{
struct as3722_softc *sc;
uint8_t ctrl, mode, iosf;
int rv;
sc = device_get_softc(dev);
if (pin >= sc->gpio_npins)
return (EINVAL);
GPIO_LOCK(sc);
ctrl = sc->gpio_pins[pin]->pin_ctrl_reg;
iosf = (ctrl >> AS3722_GPIO_IOSF_SHIFT) & AS3722_GPIO_IOSF_MASK;
/* Is pin in GPIO mode ? */
if (iosf != AS3722_IOSF_GPIO) {
GPIO_UNLOCK(sc);
return (ENXIO);
}
mode = as3722_gpio_get_mode(sc, pin, flags);
ctrl &= ~(AS3722_GPIO_MODE_MASK << AS3722_GPIO_MODE_SHIFT);
ctrl |= AS3722_MODE_PUSH_PULL << AS3722_GPIO_MODE_SHIFT;
rv = 0;
if (ctrl != sc->gpio_pins[pin]->pin_ctrl_reg) {
rv = WR1(sc, AS3722_GPIO0_CONTROL + pin, ctrl);
sc->gpio_pins[pin]->pin_ctrl_reg = ctrl;
}
GPIO_UNLOCK(sc);
return (rv);
}
int
as3722_gpio_pin_set(device_t dev, uint32_t pin, uint32_t val)
{
struct as3722_softc *sc;
uint8_t tmp;
int rv;
sc = device_get_softc(dev);
if (pin >= sc->gpio_npins)
return (EINVAL);
tmp = (val != 0) ? 1 : 0;
if (sc->gpio_pins[pin]->pin_ctrl_reg & AS3722_GPIO_INVERT)
tmp ^= 1;
GPIO_LOCK(sc);
rv = RM1(sc, AS3722_GPIO_SIGNAL_OUT, (1 << pin), (tmp << pin));
GPIO_UNLOCK(sc);
return (rv);
}
int
as3722_gpio_pin_get(device_t dev, uint32_t pin, uint32_t *val)
{
struct as3722_softc *sc;
uint8_t tmp, mode, ctrl;
int rv;
sc = device_get_softc(dev);
if (pin >= sc->gpio_npins)
return (EINVAL);
GPIO_LOCK(sc);
ctrl = sc->gpio_pins[pin]->pin_ctrl_reg;
mode = (ctrl >> AS3722_GPIO_MODE_SHIFT) & AS3722_GPIO_MODE_MASK;
if ((mode == AS3722_MODE_PUSH_PULL) ||
(mode == AS3722_MODE_PUSH_PULL_LV))
rv = RD1(sc, AS3722_GPIO_SIGNAL_OUT, &tmp);
else
rv = RD1(sc, AS3722_GPIO_SIGNAL_IN, &tmp);
GPIO_UNLOCK(sc);
if (rv != 0)
return (rv);
*val = tmp & (1 << pin) ? 1 : 0;
if (ctrl & AS3722_GPIO_INVERT)
*val ^= 1;
return (0);
}
int
as3722_gpio_pin_toggle(device_t dev, uint32_t pin)
{
struct as3722_softc *sc;
uint8_t tmp;
int rv;
sc = device_get_softc(dev);
if (pin >= sc->gpio_npins)
return (EINVAL);
GPIO_LOCK(sc);
rv = RD1(sc, AS3722_GPIO_SIGNAL_OUT, &tmp);
if (rv != 0) {
GPIO_UNLOCK(sc);
return (rv);
}
tmp ^= (1 <gpio_lock, "AS3722 GPIO lock");
sc->gpio_npins = NGPIO;
sc->gpio_pins = malloc(sizeof(struct as3722_gpio_pin *) *
sc->gpio_npins, M_AS3722_GPIO, M_WAITOK | M_ZERO);
sc->gpio_busdev = gpiobus_attach_bus(sc->dev);
if (sc->gpio_busdev == NULL)
return (ENXIO);
for (i = 0; i < sc->gpio_npins; i++) {
sc->gpio_pins[i] = malloc(sizeof(struct as3722_gpio_pin),
M_AS3722_GPIO, M_WAITOK | M_ZERO);
pin = sc->gpio_pins[i];
sprintf(pin->pin_name, "gpio%d", i);
pin->pin_caps = GPIO_PIN_INPUT | GPIO_PIN_OUTPUT |
GPIO_PIN_OPENDRAIN | GPIO_PIN_PUSHPULL | GPIO_PIN_TRISTATE |
GPIO_PIN_PULLUP | GPIO_PIN_PULLDOWN | GPIO_PIN_INVIN |
GPIO_PIN_INVOUT;
rv = RD1(sc, AS3722_GPIO0_CONTROL + i, &pin->pin_ctrl_reg);
if (rv != 0) {
device_printf(sc->dev,
"Cannot read configuration for pin %s\n",
sc->gpio_pins[i]->pin_name);
}
}
return (0);
}
Index: head/sys/arm/nvidia/tegra124/tegra124_xusbpadctl.c
===================================================================
--- head/sys/arm/nvidia/tegra124/tegra124_xusbpadctl.c (revision 331923)
+++ head/sys/arm/nvidia/tegra124/tegra124_xusbpadctl.c (revision 331924)
@@ -1,1212 +1,1212 @@
/*-
* Copyright (c) 2016 Michal Meloun
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include
__FBSDID("$FreeBSD$");
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "phydev_if.h"
/* FUSE calibration data. */
#define FUSE_XUSB_CALIB 0x0F0
#define FUSE_XUSB_CALIB_HS_CURR_LEVEL_123(x) (((x) >> 15) & 0x3F);
#define FUSE_XUSB_CALIB_HS_IREF_CAP(x) (((x) >> 13) & 0x03);
#define FUSE_XUSB_CALIB_HS_SQUELCH_LEVEL(x) (((x) >> 11) & 0x03);
#define FUSE_XUSB_CALIB_HS_TERM_RANGE_ADJ(x) (((x) >> 7) & 0x0F);
#define FUSE_XUSB_CALIB_HS_CURR_LEVEL_0(x) (((x) >> 0) & 0x3F);
/* Registers. */
#define XUSB_PADCTL_USB2_PAD_MUX 0x004
#define XUSB_PADCTL_USB2_PORT_CAP 0x008
#define USB2_PORT_CAP_ULPI_PORT_INTERNAL (1 << 25)
#define USB2_PORT_CAP_ULPI_PORT_CAP (1 << 24)
#define USB2_PORT_CAP_PORT_REVERSE_ID(p) (1 << (3 + (p) * 4))
#define USB2_PORT_CAP_PORT_INTERNAL(p) (1 << (2 + (p) * 4))
#define USB2_PORT_CAP_PORT_CAP(p, x) (((x) & 3) << ((p) * 4))
#define USB2_PORT_CAP_PORT_CAP_OTG 0x3
#define USB2_PORT_CAP_PORT_CAP_DEVICE 0x2
#define USB2_PORT_CAP_PORT_CAP_HOST 0x1
#define USB2_PORT_CAP_PORT_CAP_DISABLED 0x0
#define XUSB_PADCTL_SS_PORT_MAP 0x014
#define SS_PORT_MAP_PORT_INTERNAL(p) (1 << (3 + (p) * 4))
#define SS_PORT_MAP_PORT_MAP(p, x) (((x) & 7) << ((p) * 4))
#define XUSB_PADCTL_ELPG_PROGRAM 0x01C
#define ELPG_PROGRAM_AUX_MUX_LP0_VCORE_DOWN (1 << 26)
#define ELPG_PROGRAM_AUX_MUX_LP0_CLAMP_EN_EARLY (1 << 25)
#define ELPG_PROGRAM_AUX_MUX_LP0_CLAMP_EN (1 << 24)
#define ELPG_PROGRAM_SSP_ELPG_VCORE_DOWN(x) (1 << (18 + (x) * 4))
#define ELPG_PROGRAM_SSP_ELPG_CLAMP_EN_EARLY(x) (1 << (17 + (x) * 4))
#define ELPG_PROGRAM_SSP_ELPG_CLAMP_EN(x) (1 << (16 + (x) * 4))
#define XUSB_PADCTL_IOPHY_PLL_P0_CTL1 0x040
#define IOPHY_PLL_P0_CTL1_PLL0_LOCKDET (1 << 19)
#define IOPHY_PLL_P0_CTL1_REFCLK_SEL(x) (((x) & 0xF) << 12)
#define IOPHY_PLL_P0_CTL1_PLL_RST (1 << 1)
#define XUSB_PADCTL_IOPHY_PLL_P0_CTL2 0x044
#define IOPHY_PLL_P0_CTL2_REFCLKBUF_EN (1 << 6)
#define IOPHY_PLL_P0_CTL2_TXCLKREF_EN (1 << 5)
#define IOPHY_PLL_P0_CTL2_TXCLKREF_SEL (1 << 4)
#define XUSB_PADCTL_IOPHY_USB3_PAD_CTL2(x) (0x058 + (x) * 4)
#define IOPHY_USB3_PAD_CTL2_CDR_CNTL(x) (((x) & 0x00FF) << 4)
#define IOPHY_USB3_PAD_CTL2_RX_EQ(x) (((x) & 0xFFFF) << 8)
#define IOPHY_USB3_PAD_CTL2_RX_WANDER(x) (((x) & 0x000F) << 4)
#define IOPHY_USB3_PAD_CTL2_RX_TERM_CNTL(x) (((x) & 0x0003) << 2)
#define IOPHY_USB3_PAD_CTL2_TX_TERM_CNTL(x) (((x) & 0x0003) << 0)
#define XUSB_PADCTL_IOPHY_USB3_PAD_CTL4(x) (0x068 + (x) * 4)
#define XUSB_PADCTL_USB2_OTG_PAD_CTL0(x) (0x0A0 + (x) * 4)
#define USB2_OTG_PAD_CTL0_LSBIAS_SEL (1 << 23)
#define USB2_OTG_PAD_CTL0_DISCON_DETECT_METHOD (1 << 22)
#define USB2_OTG_PAD_CTL0_PD_ZI (1 << 21)
#define USB2_OTG_PAD_CTL0_PD2 (1 << 20)
#define USB2_OTG_PAD_CTL0_PD (1 << 19)
#define USB2_OTG_PAD_CTL0_TERM_EN (1 << 18)
#define USB2_OTG_PAD_CTL0_LS_LS_FSLEW(x) (((x) & 0x03) << 16)
#define USB2_OTG_PAD_CTL0_LS_RSLEW(x) (((x) & 0x03) << 14)
#define USB2_OTG_PAD_CTL0_FS_SLEW(x) (((x) & 0x03) << 12)
#define USB2_OTG_PAD_CTL0_HS_SLEW(x) (((x) & 0x3F) << 6)
#define USB2_OTG_PAD_CTL0_HS_CURR_LEVEL(x) (((x) & 0x3F) << 0)
#define XUSB_PADCTL_USB2_OTG_PAD_CTL1(x) (0x0AC + (x) * 4)
#define USB2_OTG_PAD_CTL1_RPU_RANGE_ADJ(x) (((x) & 0x3) << 11)
#define USB2_OTG_PAD_CTL1_HS_IREF_CAP(x) (((x) & 0x3) << 9)
#define USB2_OTG_PAD_CTL1_SPARE(x) (((x) & 0x3) << 7)
#define USB2_OTG_PAD_CTL1_TERM_RANGE_ADJ(x) (((x) & 0xF) << 3)
#define USB2_OTG_PAD_CTL1_PD_DR (1 << 2)
#define USB2_OTG_PAD_CTL1_PD_DISC_FORCE_POWERUP (1 << 1)
#define USB2_OTG_PAD_CTL1_PD_CHRP_FORCE_POWERUP (1 << 0)
#define XUSB_PADCTL_USB2_BIAS_PAD_CTL0 0x0B8
#define USB2_BIAS_PAD_CTL0_ADJRPU(x) (((x) & 0x7) << 14)
#define USB2_BIAS_PAD_CTL0_PD_TRK (1 << 13)
#define USB2_BIAS_PAD_CTL0_PD (1 << 12)
#define USB2_BIAS_PAD_CTL0_TERM_OFFSETL(x) (((x) & 0x3) << 9)
#define USB2_BIAS_PAD_CTL0_VBUS_LEVEL(x) (((x) & 0x3) << 7)
#define USB2_BIAS_PAD_CTL0_HS_CHIRP_LEVEL(x) (((x) & 0x3) << 5)
#define USB2_BIAS_PAD_CTL0_HS_DISCON_LEVEL(x) (((x) & 0x7) << 2)
#define USB2_BIAS_PAD_CTL0_HS_SQUELCH_LEVEL(x) (((x) & 0x3) << 0)
#define XUSB_PADCTL_HSIC_PAD0_CTL0 0x0C8
#define HSIC_PAD0_CTL0_HSIC_OPT(x) (((x) & 0xF) << 16)
#define HSIC_PAD0_CTL0_TX_SLEWN(x) (((x) & 0xF) << 12)
#define HSIC_PAD0_CTL0_TX_SLEWP(x) (((x) & 0xF) << 8)
#define HSIC_PAD0_CTL0_TX_RTUNEN(x) (((x) & 0xF) << 4)
#define HSIC_PAD0_CTL0_TX_RTUNEP(x) (((x) & 0xF) << 0)
#define XUSB_PADCTL_USB3_PAD_MUX 0x134
#define USB3_PAD_MUX_PCIE_IDDQ_DISABLE(x) (1 << (1 + (x)))
#define USB3_PAD_MUX_SATA_IDDQ_DISABLE (1 << 6)
#define XUSB_PADCTL_IOPHY_PLL_S0_CTL1 0x138
#define IOPHY_PLL_S0_CTL1_PLL1_LOCKDET (1 << 27)
#define IOPHY_PLL_S0_CTL1_PLL1_MODE (1 << 24)
#define IOPHY_PLL_S0_CTL1_PLL_PWR_OVRD (1 << 3)
#define IOPHY_PLL_S0_CTL1_PLL_RST_L (1 << 1)
#define IOPHY_PLL_S0_CTL1_PLL_IDDQ (1 << 0)
#define XUSB_PADCTL_IOPHY_PLL_S0_CTL2 0x13C
#define XUSB_PADCTL_IOPHY_PLL_S0_CTL3 0x140
#define XUSB_PADCTL_IOPHY_PLL_S0_CTL4 0x144
#define XUSB_PADCTL_IOPHY_MISC_PAD_S0_CTL1 0x148
#define IOPHY_MISC_PAD_S0_CTL1_IDDQ_OVRD (1 << 1)
#define IOPHY_MISC_PAD_S0_CTL1_IDDQ (1 << 0)
#define XUSB_PADCTL_IOPHY_MISC_PAD_S0_CTL2 0x14C
#define XUSB_PADCTL_IOPHY_MISC_PAD_S0_CTL3 0x150
#define XUSB_PADCTL_IOPHY_MISC_PAD_S0_CTL4 0x154
#define XUSB_PADCTL_IOPHY_MISC_PAD_S0_CTL5 0x158
#define XUSB_PADCTL_IOPHY_MISC_PAD_S0_CTL6 0x15C
#define WR4(_sc, _r, _v) bus_write_4((_sc)->mem_res, (_r), (_v))
#define RD4(_sc, _r) bus_read_4((_sc)->mem_res, (_r))
struct padctl_softc {
device_t dev;
struct resource *mem_res;
hwreset_t rst;
int phy_ena_cnt;
/* Fuses calibration data */
uint32_t hs_curr_level_0;
uint32_t hs_curr_level_123;
uint32_t hs_iref_cap;
uint32_t hs_term_range_adj;
uint32_t hs_squelch_level;
uint32_t hs_curr_level_offset;
};
static struct ofw_compat_data compat_data[] = {
{"nvidia,tegra124-xusb-padctl", 1},
{NULL, 0},
};
/* Ports. */
enum padctl_port_type {
PADCTL_PORT_USB2,
PADCTL_PORT_ULPI,
PADCTL_PORT_HSIC,
PADCTL_PORT_USB3,
};
struct padctl_lane;
struct padctl_port {
enum padctl_port_type type;
const char *name;
const char *base_name;
int idx;
int (*init)(struct padctl_softc *sc,
struct padctl_port *port);
/* Runtime data. */
bool enabled;
regulator_t supply_vbus; /* USB2, USB3 */
bool internal; /* ULPI, USB2, USB3 */
uint32_t companion; /* USB3 */
struct padctl_lane *lane;
};
static int usb3_port_init(struct padctl_softc *sc, struct padctl_port *port);
#define PORT(t, n, p, i) { \
.type = t, \
.name = n "-" #p, \
.base_name = n, \
.idx = p, \
.init = i, \
}
static struct padctl_port ports_tbl[] = {
PORT(PADCTL_PORT_USB2, "usb2", 0, NULL),
PORT(PADCTL_PORT_USB2, "usb2", 1, NULL),
PORT(PADCTL_PORT_USB2, "usb2", 2, NULL),
PORT(PADCTL_PORT_ULPI, "ulpi", 0, NULL),
PORT(PADCTL_PORT_HSIC, "hsic", 0, NULL),
PORT(PADCTL_PORT_HSIC, "hsic", 1, NULL),
PORT(PADCTL_PORT_USB3, "usb3", 0, usb3_port_init),
PORT(PADCTL_PORT_USB3, "usb3", 1, usb3_port_init),
};
/* Pads - a group of lannes. */
enum padctl_pad_type {
PADCTL_PAD_USB2,
PADCTL_PAD_ULPI,
PADCTL_PAD_HSIC,
PADCTL_PAD_PCIE,
PADCTL_PAD_SATA,
};
struct padctl_lane;
struct padctl_pad {
const char *name;
enum padctl_pad_type type;
int (*powerup)(struct padctl_softc *sc,
struct padctl_lane *lane);
int (*powerdown)(struct padctl_softc *sc,
struct padctl_lane *lane);
/* Runtime data. */
bool enabled;
struct padctl_lane *lanes[8]; /* Safe maximum value. */
int nlanes;
};
static int usb2_powerup(struct padctl_softc *sc, struct padctl_lane *lane);
static int usb2_powerdown(struct padctl_softc *sc, struct padctl_lane *lane);
static int pcie_powerup(struct padctl_softc *sc, struct padctl_lane *lane);
static int pcie_powerdown(struct padctl_softc *sc, struct padctl_lane *lane);
static int sata_powerup(struct padctl_softc *sc, struct padctl_lane *lane);
static int sata_powerdown(struct padctl_softc *sc, struct padctl_lane *lane);
#define PAD(n, t, u, d) { \
.name = n, \
.type = t, \
.powerup = u, \
.powerdown = d, \
}
static struct padctl_pad pads_tbl[] = {
PAD("usb2", PADCTL_PAD_USB2, usb2_powerup, usb2_powerdown),
PAD("ulpi", PADCTL_PAD_ULPI, NULL, NULL),
PAD("hsic", PADCTL_PAD_HSIC, NULL, NULL),
PAD("pcie", PADCTL_PAD_PCIE, pcie_powerup, pcie_powerdown),
PAD("sata", PADCTL_PAD_SATA, sata_powerup, sata_powerdown),
};
/* Lanes. */
static char *otg_mux[] = {"snps", "xusb", "uart", "rsvd"};
static char *usb_mux[] = {"snps", "xusb"};
static char *pci_mux[] = {"pcie", "usb3-ss", "sata", "rsvd"};
struct padctl_lane {
const char *name;
int idx;
bus_size_t reg;
uint32_t shift;
uint32_t mask;
char **mux;
int nmux;
/* Runtime data. */
bool enabled;
struct padctl_pad *pad;
struct padctl_port *port;
int mux_idx;
};
#define LANE(n, p, r, s, m, mx) { \
.name = n "-" #p, \
.idx = p, \
.reg = r, \
.shift = s, \
.mask = m, \
.mux = mx, \
.nmux = nitems(mx), \
}
static struct padctl_lane lanes_tbl[] = {
LANE("usb2", 0, XUSB_PADCTL_USB2_PAD_MUX, 0, 0x3, otg_mux),
LANE("usb2", 1, XUSB_PADCTL_USB2_PAD_MUX, 2, 0x3, otg_mux),
LANE("usb2", 2, XUSB_PADCTL_USB2_PAD_MUX, 4, 0x3, otg_mux),
LANE("ulpi", 0, XUSB_PADCTL_USB2_PAD_MUX, 12, 0x1, usb_mux),
LANE("hsic", 0, XUSB_PADCTL_USB2_PAD_MUX, 14, 0x1, usb_mux),
LANE("hsic", 1, XUSB_PADCTL_USB2_PAD_MUX, 15, 0x1, usb_mux),
LANE("pcie", 0, XUSB_PADCTL_USB3_PAD_MUX, 16, 0x3, pci_mux),
LANE("pcie", 1, XUSB_PADCTL_USB3_PAD_MUX, 18, 0x3, pci_mux),
LANE("pcie", 2, XUSB_PADCTL_USB3_PAD_MUX, 20, 0x3, pci_mux),
LANE("pcie", 3, XUSB_PADCTL_USB3_PAD_MUX, 22, 0x3, pci_mux),
LANE("pcie", 4, XUSB_PADCTL_USB3_PAD_MUX, 24, 0x3, pci_mux),
LANE("sata", 0, XUSB_PADCTL_USB3_PAD_MUX, 26, 0x3, pci_mux),
};
/* Define all possible mappings for USB3 port lanes */
struct padctl_lane_map {
int port_idx;
enum padctl_pad_type pad_type;
int lane_idx;
};
#define LANE_MAP(pi, pt, li) { \
.port_idx = pi, \
.pad_type = pt, \
.lane_idx = li, \
}
static struct padctl_lane_map lane_map_tbl[] = {
LANE_MAP(0, PADCTL_PAD_PCIE, 0), /* port USB3-0 -> lane PCIE-0 */
LANE_MAP(1, PADCTL_PAD_PCIE, 1), /* port USB3-1 -> lane PCIE-1 */
/* -- or -- */
LANE_MAP(1, PADCTL_PAD_SATA, 0), /* port USB3-1 -> lane SATA-0 */
};
/* Phy class and methods. */
static int xusbpadctl_phy_enable(struct phynode *phy, bool enable);
static phynode_method_t xusbpadctl_phynode_methods[] = {
PHYNODEMETHOD(phynode_enable, xusbpadctl_phy_enable),
PHYNODEMETHOD_END
};
DEFINE_CLASS_1(xusbpadctl_phynode, xusbpadctl_phynode_class,
xusbpadctl_phynode_methods, 0, phynode_class);
static struct padctl_port *search_lane_port(struct padctl_softc *sc,
struct padctl_lane *lane);
/* -------------------------------------------------------------------------
*
* PHY functions
*/
static int
usb3_port_init(struct padctl_softc *sc, struct padctl_port *port)
{
uint32_t reg;
reg = RD4(sc, XUSB_PADCTL_SS_PORT_MAP);
if (port->internal)
reg &= ~SS_PORT_MAP_PORT_INTERNAL(port->idx);
else
reg |= SS_PORT_MAP_PORT_INTERNAL(port->idx);
reg &= ~SS_PORT_MAP_PORT_MAP(port->idx, ~0);
reg |= SS_PORT_MAP_PORT_MAP(port->idx, port->companion);
WR4(sc, XUSB_PADCTL_SS_PORT_MAP, reg);
reg = RD4(sc, XUSB_PADCTL_IOPHY_USB3_PAD_CTL2(port->idx));
reg &= ~IOPHY_USB3_PAD_CTL2_CDR_CNTL(~0);
reg &= ~IOPHY_USB3_PAD_CTL2_RX_EQ(~0);
reg &= ~IOPHY_USB3_PAD_CTL2_RX_WANDER(~0);
reg |= IOPHY_USB3_PAD_CTL2_CDR_CNTL(0x24);
reg |= IOPHY_USB3_PAD_CTL2_RX_EQ(0xF070);
reg |= IOPHY_USB3_PAD_CTL2_RX_WANDER(0xF);
WR4(sc, XUSB_PADCTL_IOPHY_USB3_PAD_CTL2(port->idx), reg);
WR4(sc, XUSB_PADCTL_IOPHY_USB3_PAD_CTL4(port->idx),
0x002008EE);
reg = RD4(sc, XUSB_PADCTL_ELPG_PROGRAM);
reg &= ~ELPG_PROGRAM_SSP_ELPG_VCORE_DOWN(port->idx);
WR4(sc, XUSB_PADCTL_ELPG_PROGRAM, reg);
DELAY(100);
reg = RD4(sc, XUSB_PADCTL_ELPG_PROGRAM);
reg &= ~ELPG_PROGRAM_SSP_ELPG_CLAMP_EN_EARLY(port->idx);
WR4(sc, XUSB_PADCTL_ELPG_PROGRAM, reg);
DELAY(100);
reg = RD4(sc, XUSB_PADCTL_ELPG_PROGRAM);
reg &= ~ELPG_PROGRAM_SSP_ELPG_CLAMP_EN(port->idx);
WR4(sc, XUSB_PADCTL_ELPG_PROGRAM, reg);
DELAY(100);
return (0);
}
static int
pcie_powerup(struct padctl_softc *sc, struct padctl_lane *lane)
{
uint32_t reg;
int i;
reg = RD4(sc, XUSB_PADCTL_IOPHY_PLL_P0_CTL1);
reg &= ~IOPHY_PLL_P0_CTL1_REFCLK_SEL(~0);
WR4(sc, XUSB_PADCTL_IOPHY_PLL_P0_CTL1, reg);
DELAY(100);
reg = RD4(sc, XUSB_PADCTL_IOPHY_PLL_P0_CTL2);
reg |= IOPHY_PLL_P0_CTL2_REFCLKBUF_EN;
reg |= IOPHY_PLL_P0_CTL2_TXCLKREF_EN;
reg |= IOPHY_PLL_P0_CTL2_TXCLKREF_SEL;
WR4(sc, XUSB_PADCTL_IOPHY_PLL_P0_CTL2, reg);
DELAY(100);
reg = RD4(sc, XUSB_PADCTL_IOPHY_PLL_P0_CTL1);
reg |= IOPHY_PLL_P0_CTL1_PLL_RST;
WR4(sc, XUSB_PADCTL_IOPHY_PLL_P0_CTL1, reg);
DELAY(100);
for (i = 100; i > 0; i--) {
reg = RD4(sc, XUSB_PADCTL_IOPHY_PLL_P0_CTL1);
if (reg & IOPHY_PLL_P0_CTL1_PLL0_LOCKDET)
break;
DELAY(10);
}
if (i <= 0) {
device_printf(sc->dev, "Failed to power up PCIe phy\n");
return (ETIMEDOUT);
}
reg = RD4(sc, XUSB_PADCTL_USB3_PAD_MUX);
reg |= USB3_PAD_MUX_PCIE_IDDQ_DISABLE(lane->idx);
WR4(sc, XUSB_PADCTL_USB3_PAD_MUX, reg);
return (0);
}
static int
pcie_powerdown(struct padctl_softc *sc, struct padctl_lane *lane)
{
uint32_t reg;
reg = RD4(sc, XUSB_PADCTL_USB3_PAD_MUX);
reg &= ~USB3_PAD_MUX_PCIE_IDDQ_DISABLE(lane->idx);
WR4(sc, XUSB_PADCTL_USB3_PAD_MUX, reg);
reg = RD4(sc, XUSB_PADCTL_IOPHY_PLL_P0_CTL1);
reg &= ~IOPHY_PLL_P0_CTL1_PLL_RST;
WR4(sc, XUSB_PADCTL_IOPHY_PLL_P0_CTL1, reg);
DELAY(100);
return (0);
}
static int
sata_powerup(struct padctl_softc *sc, struct padctl_lane *lane)
{
uint32_t reg;
int i;
reg = RD4(sc, XUSB_PADCTL_IOPHY_MISC_PAD_S0_CTL1);
reg &= ~IOPHY_MISC_PAD_S0_CTL1_IDDQ_OVRD;
reg &= ~IOPHY_MISC_PAD_S0_CTL1_IDDQ;
WR4(sc, XUSB_PADCTL_IOPHY_MISC_PAD_S0_CTL1, reg);
reg = RD4(sc, XUSB_PADCTL_IOPHY_PLL_S0_CTL1);
reg &= ~IOPHY_PLL_S0_CTL1_PLL_PWR_OVRD;
reg &= ~IOPHY_PLL_S0_CTL1_PLL_IDDQ;
WR4(sc, XUSB_PADCTL_IOPHY_PLL_S0_CTL1, reg);
reg = RD4(sc, XUSB_PADCTL_IOPHY_PLL_S0_CTL1);
reg |= IOPHY_PLL_S0_CTL1_PLL1_MODE;
WR4(sc, XUSB_PADCTL_IOPHY_PLL_S0_CTL1, reg);
reg = RD4(sc, XUSB_PADCTL_IOPHY_PLL_S0_CTL1);
reg |= IOPHY_PLL_S0_CTL1_PLL_RST_L;
WR4(sc, XUSB_PADCTL_IOPHY_PLL_S0_CTL1, reg);
for (i = 100; i >= 0; i--) {
reg = RD4(sc, XUSB_PADCTL_IOPHY_PLL_S0_CTL1);
if (reg & IOPHY_PLL_S0_CTL1_PLL1_LOCKDET)
break;
DELAY(100);
}
if (i <= 0) {
device_printf(sc->dev, "Failed to power up SATA phy\n");
return (ETIMEDOUT);
}
reg = RD4(sc, XUSB_PADCTL_USB3_PAD_MUX);
reg |= IOPHY_PLL_S0_CTL1_PLL_RST_L;
WR4(sc, XUSB_PADCTL_USB3_PAD_MUX, reg);
reg = RD4(sc, XUSB_PADCTL_USB3_PAD_MUX);
reg |= USB3_PAD_MUX_SATA_IDDQ_DISABLE;
WR4(sc, XUSB_PADCTL_USB3_PAD_MUX, reg);
return (0);
}
static int
sata_powerdown(struct padctl_softc *sc, struct padctl_lane *lane)
{
uint32_t reg;
reg = RD4(sc, XUSB_PADCTL_USB3_PAD_MUX);
reg &= ~USB3_PAD_MUX_SATA_IDDQ_DISABLE;
WR4(sc, XUSB_PADCTL_USB3_PAD_MUX, reg);
reg = RD4(sc, XUSB_PADCTL_IOPHY_PLL_S0_CTL1);
reg &= ~IOPHY_PLL_S0_CTL1_PLL_RST_L;
WR4(sc, XUSB_PADCTL_IOPHY_PLL_S0_CTL1, reg);
DELAY(100);
reg = RD4(sc, XUSB_PADCTL_IOPHY_PLL_S0_CTL1);
reg &= ~IOPHY_PLL_S0_CTL1_PLL1_MODE;
WR4(sc, XUSB_PADCTL_IOPHY_PLL_S0_CTL1, reg);
DELAY(100);
reg = RD4(sc, XUSB_PADCTL_IOPHY_PLL_S0_CTL1);
reg |= IOPHY_PLL_S0_CTL1_PLL_PWR_OVRD;
reg |= IOPHY_PLL_S0_CTL1_PLL_IDDQ;
WR4(sc, XUSB_PADCTL_IOPHY_PLL_S0_CTL1, reg);
DELAY(100);
reg = RD4(sc, XUSB_PADCTL_IOPHY_MISC_PAD_S0_CTL1);
reg |= IOPHY_MISC_PAD_S0_CTL1_IDDQ_OVRD;
reg |= IOPHY_MISC_PAD_S0_CTL1_IDDQ;
WR4(sc, XUSB_PADCTL_IOPHY_MISC_PAD_S0_CTL1, reg);
DELAY(100);
return (0);
}
static int
usb2_powerup(struct padctl_softc *sc, struct padctl_lane *lane)
{
uint32_t reg;
struct padctl_port *port;
int rv;
port = search_lane_port(sc, lane);
if (port == NULL) {
device_printf(sc->dev, "Cannot find port for lane: %s\n",
lane->name);
}
reg = RD4(sc, XUSB_PADCTL_USB2_BIAS_PAD_CTL0);
reg &= ~USB2_BIAS_PAD_CTL0_HS_SQUELCH_LEVEL(~0);
reg &= ~USB2_BIAS_PAD_CTL0_HS_DISCON_LEVEL(~0);
reg |= USB2_BIAS_PAD_CTL0_HS_SQUELCH_LEVEL(sc->hs_squelch_level);
reg |= USB2_BIAS_PAD_CTL0_HS_DISCON_LEVEL(5);
WR4(sc, XUSB_PADCTL_USB2_BIAS_PAD_CTL0, reg);
reg = RD4(sc, XUSB_PADCTL_USB2_PORT_CAP);
reg &= ~USB2_PORT_CAP_PORT_CAP(lane->idx, ~0);
reg |= USB2_PORT_CAP_PORT_CAP(lane->idx, USB2_PORT_CAP_PORT_CAP_HOST);
WR4(sc, XUSB_PADCTL_USB2_PORT_CAP, reg);
reg = RD4(sc, XUSB_PADCTL_USB2_OTG_PAD_CTL0(lane->idx));
reg &= ~USB2_OTG_PAD_CTL0_HS_CURR_LEVEL(~0);
reg &= ~USB2_OTG_PAD_CTL0_HS_SLEW(~0);
reg &= ~USB2_OTG_PAD_CTL0_LS_RSLEW(~0);
reg &= ~USB2_OTG_PAD_CTL0_PD;
reg &= ~USB2_OTG_PAD_CTL0_PD2;
reg &= ~USB2_OTG_PAD_CTL0_PD_ZI;
reg |= USB2_OTG_PAD_CTL0_HS_SLEW(14);
if (lane->idx == 0) {
reg |= USB2_OTG_PAD_CTL0_HS_CURR_LEVEL(sc->hs_curr_level_0);
reg |= USB2_OTG_PAD_CTL0_LS_RSLEW(3);
} else {
reg |= USB2_OTG_PAD_CTL0_HS_CURR_LEVEL(sc->hs_curr_level_123);
reg |= USB2_OTG_PAD_CTL0_LS_RSLEW(0);
}
WR4(sc, XUSB_PADCTL_USB2_OTG_PAD_CTL0(lane->idx), reg);
reg = RD4(sc, XUSB_PADCTL_USB2_OTG_PAD_CTL1(lane->idx));
reg &= ~USB2_OTG_PAD_CTL1_TERM_RANGE_ADJ(~0);
reg &= ~USB2_OTG_PAD_CTL1_HS_IREF_CAP(~0);
reg &= ~USB2_OTG_PAD_CTL1_PD_DR;
reg &= ~USB2_OTG_PAD_CTL1_PD_DISC_FORCE_POWERUP;
reg &= ~USB2_OTG_PAD_CTL1_PD_CHRP_FORCE_POWERUP;
reg |= USB2_OTG_PAD_CTL1_TERM_RANGE_ADJ(sc->hs_term_range_adj);
reg |= USB2_OTG_PAD_CTL1_HS_IREF_CAP(sc->hs_iref_cap);
WR4(sc, XUSB_PADCTL_USB2_OTG_PAD_CTL1(lane->idx), reg);
if (port != NULL && port->supply_vbus != NULL) {
rv = regulator_enable(port->supply_vbus);
if (rv != 0) {
device_printf(sc->dev,
"Cannot enable vbus regulator\n");
return (rv);
}
}
reg = RD4(sc, XUSB_PADCTL_USB2_BIAS_PAD_CTL0);
reg &= ~USB2_BIAS_PAD_CTL0_PD;
WR4(sc, XUSB_PADCTL_USB2_BIAS_PAD_CTL0, reg);
return (0);
}
static int
usb2_powerdown(struct padctl_softc *sc, struct padctl_lane *lane)
{
uint32_t reg;
struct padctl_port *port;
int rv;
port = search_lane_port(sc, lane);
if (port == NULL) {
device_printf(sc->dev, "Cannot find port for lane: %s\n",
lane->name);
}
reg = RD4(sc, XUSB_PADCTL_USB2_BIAS_PAD_CTL0);
reg |= USB2_BIAS_PAD_CTL0_PD;
WR4(sc, XUSB_PADCTL_USB2_BIAS_PAD_CTL0, reg);
if (port != NULL && port->supply_vbus != NULL) {
rv = regulator_enable(port->supply_vbus);
if (rv != 0) {
device_printf(sc->dev,
"Cannot disable vbus regulator\n");
return (rv);
}
}
return (0);
}
static int
phy_powerup(struct padctl_softc *sc)
{
uint32_t reg;
reg = RD4(sc, XUSB_PADCTL_ELPG_PROGRAM);
reg &= ~ELPG_PROGRAM_AUX_MUX_LP0_VCORE_DOWN;
WR4(sc, XUSB_PADCTL_ELPG_PROGRAM, reg);
DELAY(100);
reg = RD4(sc, XUSB_PADCTL_ELPG_PROGRAM);
reg &= ~ELPG_PROGRAM_AUX_MUX_LP0_CLAMP_EN;
WR4(sc, XUSB_PADCTL_ELPG_PROGRAM, reg);
DELAY(100);
reg = RD4(sc, XUSB_PADCTL_ELPG_PROGRAM);
reg &= ~ELPG_PROGRAM_AUX_MUX_LP0_CLAMP_EN_EARLY;
WR4(sc, XUSB_PADCTL_ELPG_PROGRAM, reg);
DELAY(100);
return (0);
}
static int
phy_powerdown(struct padctl_softc *sc)
{
uint32_t reg;
reg = RD4(sc, XUSB_PADCTL_ELPG_PROGRAM);
reg |= ELPG_PROGRAM_AUX_MUX_LP0_CLAMP_EN_EARLY;
WR4(sc, XUSB_PADCTL_ELPG_PROGRAM, reg);
DELAY(100);
reg = RD4(sc, XUSB_PADCTL_ELPG_PROGRAM);
reg |= ELPG_PROGRAM_AUX_MUX_LP0_CLAMP_EN;
WR4(sc, XUSB_PADCTL_ELPG_PROGRAM, reg);
DELAY(100);
reg = RD4(sc, XUSB_PADCTL_ELPG_PROGRAM);
reg |= ELPG_PROGRAM_AUX_MUX_LP0_VCORE_DOWN;
WR4(sc, XUSB_PADCTL_ELPG_PROGRAM, reg);
DELAY(100);
return (0);
}
static int
xusbpadctl_phy_enable(struct phynode *phy, bool enable)
{
device_t dev;
intptr_t id;
struct padctl_softc *sc;
struct padctl_lane *lane;
struct padctl_pad *pad;
int rv;
dev = phynode_get_device(phy);
id = phynode_get_id(phy);
sc = device_get_softc(dev);
if (id < 0 || id >= nitems(lanes_tbl)) {
device_printf(dev, "Unknown phy: %d\n", id);
return (ENXIO);
}
lane = lanes_tbl + id;
if (!lane->enabled) {
device_printf(dev, "Lane is not enabled/configured: %s\n",
lane->name);
return (ENXIO);
}
pad = lane->pad;
if (enable) {
if (sc->phy_ena_cnt == 0) {
rv = phy_powerup(sc);
if (rv != 0)
return (rv);
}
sc->phy_ena_cnt++;
}
if (enable)
rv = pad->powerup(sc, lane);
else
rv = pad->powerdown(sc, lane);
if (rv != 0)
return (rv);
if (!enable) {
if (sc->phy_ena_cnt == 1) {
rv = phy_powerdown(sc);
if (rv != 0)
return (rv);
}
sc->phy_ena_cnt--;
}
return (0);
}
/* -------------------------------------------------------------------------
*
* FDT processing
*/
static struct padctl_port *
search_port(struct padctl_softc *sc, char *port_name)
{
int i;
for (i = 0; i < nitems(ports_tbl); i++) {
if (strcmp(port_name, ports_tbl[i].name) == 0)
return (&ports_tbl[i]);
}
return (NULL);
}
static struct padctl_port *
search_lane_port(struct padctl_softc *sc, struct padctl_lane *lane)
{
int i;
for (i = 0; i < nitems(ports_tbl); i++) {
if (!ports_tbl[i].enabled)
continue;
if (ports_tbl[i].lane == lane)
return (ports_tbl + i);
}
return (NULL);
}
static struct padctl_lane *
search_lane(struct padctl_softc *sc, char *lane_name)
{
int i;
for (i = 0; i < nitems(lanes_tbl); i++) {
if (strcmp(lane_name, lanes_tbl[i].name) == 0)
return (lanes_tbl + i);
}
return (NULL);
}
static struct padctl_lane *
search_pad_lane(struct padctl_softc *sc, enum padctl_pad_type type, int idx)
{
int i;
for (i = 0; i < nitems(lanes_tbl); i++) {
if (!lanes_tbl[i].enabled)
continue;
if (type == lanes_tbl[i].pad->type && idx == lanes_tbl[i].idx)
return (lanes_tbl + i);
}
return (NULL);
}
static struct padctl_lane *
search_usb3_pad_lane(struct padctl_softc *sc, int idx)
{
int i;
struct padctl_lane *lane, *tmp;
lane = NULL;
for (i = 0; i < nitems(lane_map_tbl); i++) {
if (idx != lane_map_tbl[i].port_idx)
continue;
tmp = search_pad_lane(sc, lane_map_tbl[i].pad_type,
lane_map_tbl[i].lane_idx);
if (tmp == NULL)
continue;
if (strcmp(tmp->mux[tmp->mux_idx], "usb3-ss") != 0)
continue;
if (lane != NULL) {
device_printf(sc->dev, "Duplicated mappings found for"
" lanes: %s and %s\n", lane->name, tmp->name);
return (NULL);
}
lane = tmp;
}
return (lane);
}
static struct padctl_pad *
search_pad(struct padctl_softc *sc, char *pad_name)
{
int i;
for (i = 0; i < nitems(pads_tbl); i++) {
if (strcmp(pad_name, pads_tbl[i].name) == 0)
return (pads_tbl + i);
}
return (NULL);
}
static int
search_mux(struct padctl_softc *sc, struct padctl_lane *lane, char *fnc_name)
{
int i;
for (i = 0; i < lane->nmux; i++) {
if (strcmp(fnc_name, lane->mux[i]) == 0)
return (i);
}
return (-1);
}
static int
config_lane(struct padctl_softc *sc, struct padctl_lane *lane)
{
uint32_t reg;
reg = RD4(sc, lane->reg);
reg &= ~(lane->mask << lane->shift);
reg |= (lane->mux_idx & lane->mask) << lane->shift;
WR4(sc, lane->reg, reg);
return (0);
}
static int
process_lane(struct padctl_softc *sc, phandle_t node, struct padctl_pad *pad)
{
struct padctl_lane *lane;
struct phynode *phynode;
struct phynode_init_def phy_init;
char *name;
char *function;
int rv;
name = NULL;
function = NULL;
rv = OF_getprop_alloc(node, "name", 1, (void **)&name);
if (rv <= 0) {
device_printf(sc->dev, "Cannot read lane name.\n");
return (ENXIO);
}
lane = search_lane(sc, name);
if (lane == NULL) {
device_printf(sc->dev, "Unknown lane: %s\n", name);
rv = ENXIO;
goto end;
}
/* Read function (mux) settings. */
rv = OF_getprop_alloc(node, "nvidia,function", 1, (void **)&function);
if (rv <= 0) {
device_printf(sc->dev, "Cannot read lane function.\n");
rv = ENXIO;
goto end;
}
lane->mux_idx = search_mux(sc, lane, function);
if (lane->mux_idx == ~0) {
device_printf(sc->dev, "Unknown function %s for lane %s\n",
function, name);
rv = ENXIO;
goto end;
}
rv = config_lane(sc, lane);
if (rv != 0) {
device_printf(sc->dev, "Cannot configure lane: %s: %d\n",
name, rv);
rv = ENXIO;
goto end;
}
lane->pad = pad;
lane->enabled = true;
pad->lanes[pad->nlanes++] = lane;
/* Create and register phy. */
bzero(&phy_init, sizeof(phy_init));
phy_init.id = lane - lanes_tbl;
phy_init.ofw_node = node;
phynode = phynode_create(sc->dev, &xusbpadctl_phynode_class, &phy_init);
if (phynode == NULL) {
device_printf(sc->dev, "Cannot create phy\n");
rv = ENXIO;
goto end;
}
if (phynode_register(phynode) == NULL) {
device_printf(sc->dev, "Cannot create phy\n");
return (ENXIO);
}
rv = 0;
end:
if (name != NULL)
OF_prop_free(name);
if (function != NULL)
OF_prop_free(function);
return (rv);
}
static int
process_pad(struct padctl_softc *sc, phandle_t node)
{
struct padctl_pad *pad;
char *name;
int rv;
name = NULL;
rv = OF_getprop_alloc(node, "name", 1, (void **)&name);
if (rv <= 0) {
device_printf(sc->dev, "Cannot read pad name.\n");
return (ENXIO);
}
pad = search_pad(sc, name);
if (pad == NULL) {
device_printf(sc->dev, "Unknown pad: %s\n", name);
rv = ENXIO;
goto end;
}
/* Read and process associated lanes. */
node = ofw_bus_find_child(node, "lanes");
if (node <= 0) {
device_printf(sc->dev, "Cannot find regulators subnode\n");
rv = ENXIO;
goto end;
}
for (node = OF_child(node); node != 0; node = OF_peer(node)) {
- if (!fdt_is_enabled(node))
+ if (!ofw_bus_node_status_okay(node))
continue;
rv = process_lane(sc, node, pad);
if (rv != 0)
goto end;
}
pad->enabled = true;
rv = 0;
end:
if (name != NULL)
OF_prop_free(name);
return (rv);
}
static int
process_port(struct padctl_softc *sc, phandle_t node)
{
struct padctl_port *port;
char *name;
int rv;
name = NULL;
rv = OF_getprop_alloc(node, "name", 1, (void **)&name);
if (rv <= 0) {
device_printf(sc->dev, "Cannot read port name.\n");
return (ENXIO);
}
port = search_port(sc, name);
if (port == NULL) {
device_printf(sc->dev, "Unknown port: %s\n", name);
rv = ENXIO;
goto end;
}
if (port->type == PADCTL_PORT_USB3) {
rv = OF_getencprop(node, "nvidia,usb2-companion",
&(port->companion), sizeof(port->companion));
if (rv <= 0) {
device_printf(sc->dev,
"Missing 'nvidia,usb2-companion' property "
"for port: %s\n", name);
rv = ENXIO;
goto end;
}
}
if (OF_hasprop(node, "vbus-supply")) {
rv = regulator_get_by_ofw_property(sc->dev, 0,
"vbus-supply", &port->supply_vbus);
if (rv <= 0) {
device_printf(sc->dev,
"Cannot get 'vbus-supply' regulator "
"for port: %s\n", name);
rv = ENXIO;
goto end;
}
}
if (OF_hasprop(node, "nvidia,internal"))
port->internal = true;
/* Find assigned lane */
if (port->lane == NULL) {
switch(port->type) {
/* Routing is fixed for USB2, ULPI AND HSIC. */
case PADCTL_PORT_USB2:
port->lane = search_pad_lane(sc, PADCTL_PAD_USB2,
port->idx);
break;
case PADCTL_PORT_ULPI:
port->lane = search_pad_lane(sc, PADCTL_PAD_ULPI,
port->idx);
break;
case PADCTL_PORT_HSIC:
port->lane = search_pad_lane(sc, PADCTL_PAD_HSIC,
port->idx);
break;
case PADCTL_PORT_USB3:
port->lane = search_usb3_pad_lane(sc, port->idx);
break;
}
}
if (port->lane == NULL) {
device_printf(sc->dev, "Cannot find lane for port: %s\n", name);
rv = ENXIO;
goto end;
}
port->enabled = true;
rv = 0;
end:
if (name != NULL)
OF_prop_free(name);
return (rv);
}
static int
parse_fdt(struct padctl_softc *sc, phandle_t base_node)
{
phandle_t node;
int rv;
rv = 0;
node = ofw_bus_find_child(base_node, "pads");
if (node <= 0) {
device_printf(sc->dev, "Cannot find pads subnode.\n");
return (ENXIO);
}
for (node = OF_child(node); node != 0; node = OF_peer(node)) {
- if (!fdt_is_enabled(node))
+ if (!ofw_bus_node_status_okay(node))
continue;
rv = process_pad(sc, node);
if (rv != 0)
return (rv);
}
node = ofw_bus_find_child(base_node, "ports");
if (node <= 0) {
device_printf(sc->dev, "Cannot find ports subnode.\n");
return (ENXIO);
}
for (node = OF_child(node); node != 0; node = OF_peer(node)) {
- if (!fdt_is_enabled(node))
+ if (!ofw_bus_node_status_okay(node))
continue;
rv = process_port(sc, node);
if (rv != 0)
return (rv);
}
return (0);
}
static void
load_calibration(struct padctl_softc *sc)
{
uint32_t reg;
/* All XUSB pad calibrations are packed into single dword.*/
reg = tegra_fuse_read_4(FUSE_XUSB_CALIB);
sc->hs_curr_level_0 = FUSE_XUSB_CALIB_HS_CURR_LEVEL_0(reg);
sc->hs_curr_level_123 = FUSE_XUSB_CALIB_HS_CURR_LEVEL_123(reg);
sc->hs_iref_cap = FUSE_XUSB_CALIB_HS_IREF_CAP(reg);
sc->hs_squelch_level = FUSE_XUSB_CALIB_HS_SQUELCH_LEVEL(reg);
sc->hs_term_range_adj = FUSE_XUSB_CALIB_HS_TERM_RANGE_ADJ(reg);
}
/* -------------------------------------------------------------------------
*
* BUS functions
*/
static int
xusbpadctl_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 XUSB phy");
return (BUS_PROBE_DEFAULT);
}
static int
xusbpadctl_detach(device_t dev)
{
/* This device is always present. */
return (EBUSY);
}
static int
xusbpadctl_attach(device_t dev)
{
struct padctl_softc * sc;
int i, rid, rv;
struct padctl_port *port;
phandle_t node;
sc = device_get_softc(dev);
sc->dev = dev;
node = ofw_bus_get_node(dev);
rid = 0;
sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
RF_ACTIVE);
if (sc->mem_res == NULL) {
device_printf(dev, "Cannot allocate memory resources\n");
return (ENXIO);
}
rv = hwreset_get_by_ofw_name(dev, 0, "padctl", &sc->rst);
if (rv != 0) {
device_printf(dev, "Cannot get 'padctl' reset: %d\n", rv);
return (rv);
}
rv = hwreset_deassert(sc->rst);
if (rv != 0) {
device_printf(dev, "Cannot unreset 'padctl' reset: %d\n", rv);
return (rv);
}
load_calibration(sc);
rv = parse_fdt(sc, node);
if (rv != 0) {
device_printf(dev, "Cannot parse fdt configuration: %d\n", rv);
return (rv);
}
for (i = 0; i < nitems(ports_tbl); i++) {
port = ports_tbl + i;
if (!port->enabled)
continue;
if (port->init == NULL)
continue;
rv = port->init(sc, port);
if (rv != 0) {
device_printf(dev, "Cannot init port '%s'\n",
port->name);
return (rv);
}
}
return (0);
}
static device_method_t tegra_xusbpadctl_methods[] = {
/* Device interface */
DEVMETHOD(device_probe, xusbpadctl_probe),
DEVMETHOD(device_attach, xusbpadctl_attach),
DEVMETHOD(device_detach, xusbpadctl_detach),
DEVMETHOD_END
};
static devclass_t tegra_xusbpadctl_devclass;
static DEFINE_CLASS_0(xusbpadctl, tegra_xusbpadctl_driver,
tegra_xusbpadctl_methods, sizeof(struct padctl_softc));
EARLY_DRIVER_MODULE(tegra_xusbpadctl, simplebus, tegra_xusbpadctl_driver,
tegra_xusbpadctl_devclass, NULL, NULL, 73);
Index: head/sys/arm/nvidia/tegra_pinmux.c
===================================================================
--- head/sys/arm/nvidia/tegra_pinmux.c (revision 331923)
+++ head/sys/arm/nvidia/tegra_pinmux.c (revision 331924)
@@ -1,799 +1,799 @@
/*-
* Copyright (c) 2016 Michal Meloun
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include
__FBSDID("$FreeBSD$");
/*
* Pin multiplexer driver for Tegra SoCs.
*/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
/* 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;
struct resource *mipi_mem_res;
};
static struct ofw_compat_data compat_data[] = {
{"nvidia,tegra124-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,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_BANK_FF 31
#define GPIO_NUM(b, p) (8 * (b) + (p))
struct tegra_mux {
char *name;
bus_size_t reg;
char *functions[4];
int gpio_num;
};
#define GMUX(r, gb, gi, nm, f1, f2, f3, f4) \
{ \
.name = #nm, \
.reg = r, \
.gpio_num = GPIO_NUM(GPIO_BANK_##gb, gi), \
.functions = {#f1, #f2, #f3, #f4}, \
}
#define FMUX(r, nm, f1, f2, f3, f4) \
{ \
.name = #nm, \
.reg = r, \
.gpio_num = -1, \
.functions = {#f1, #f2, #f3, #f4}, \
}
static const struct tegra_mux pin_mux_tbl[] = {
GMUX(0x000, O, 1, ulpi_data0_po1, spi3, hsi, uarta, ulpi),
GMUX(0x004, O, 2, ulpi_data1_po2, spi3, hsi, uarta, ulpi),
GMUX(0x008, O, 3, ulpi_data2_po3, spi3, hsi, uarta, ulpi),
GMUX(0x00C, O, 4, ulpi_data3_po4, spi3, hsi, uarta, ulpi),
GMUX(0x010, O, 5, ulpi_data4_po5, spi2, hsi, uarta, ulpi),
GMUX(0x014, O, 6, ulpi_data5_po6, spi2, hsi, uarta, ulpi),
GMUX(0x018, O, 7, ulpi_data6_po7, spi2, hsi, uarta, ulpi),
GMUX(0x01C, O, 0, ulpi_data7_po0, spi2, hsi, uarta, ulpi),
GMUX(0x020, P, 9, ulpi_clk_py0, spi1, spi5, uartd, ulpi),
GMUX(0x024, P, 1, ulpi_dir_py1, spi1, spi5, uartd, ulpi),
GMUX(0x028, P, 2, ulpi_nxt_py2, spi1, spi5, uartd, ulpi),
GMUX(0x02C, P, 3, ulpi_stp_py3, spi1, spi5, uartd, ulpi),
GMUX(0x030, P, 0, dap3_fs_pp0, i2s2, spi5, displaya, displayb),
GMUX(0x034, P, 1, dap3_din_pp1, i2s2, spi5, displaya, displayb),
GMUX(0x038, P, 2, dap3_dout_pp2, i2s2, spi5, displaya, rsvd4),
GMUX(0x03C, P, 3, dap3_sclk_pp3, i2s2, spi5, rsvd3, displayb),
GMUX(0x040, V, 0, pv0, rsvd1, rsvd2, rsvd3, rsvd4),
GMUX(0x044, V, 1, pv1, rsvd1, rsvd2, rsvd3, rsvd4),
GMUX(0x048, Z, 0, sdmmc1_clk_pz0, sdmmc1, clk12, rsvd3, rsvd4),
GMUX(0x04C, Z, 1, sdmmc1_cmd_pz1, sdmmc1, spdif, spi4, uarta),
GMUX(0x050, Y, 4, sdmmc1_dat3_py4, sdmmc1, spdif, spi4, uarta),
GMUX(0x054, Y, 5, sdmmc1_dat2_py5, sdmmc1, pwm0, spi4, uarta),
GMUX(0x058, Y, 6, sdmmc1_dat1_py6, sdmmc1, pwm1, spi4, uarta),
GMUX(0x05C, Y, 7, sdmmc1_dat0_py7, sdmmc1, rsvd2, spi4, uarta),
GMUX(0x068, W, 5, clk2_out_pw5, extperiph2, rsvd2, rsvd3, rsvd4),
GMUX(0x06C, CC, 5, clk2_req_pcc5, dap, rsvd2, rsvd3, rsvd4),
GMUX(0x110, N, 7, hdmi_int_pn7, rsvd1, rsvd2, rsvd3, rsvd4),
GMUX(0x114, V, 4, ddc_scl_pv4, i2c4, rsvd2, rsvd3, rsvd4),
GMUX(0x118, V, 5, ddc_sda_pv5, i2c4, rsvd2, rsvd3, rsvd4),
GMUX(0x164, V, 3, uart2_rxd_pc3, irda, spdif, uarta, spi4),
GMUX(0x168, C, 2, uart2_txd_pc2, irda, spdif, uarta, spi4),
GMUX(0x16C, J, 6, uart2_rts_n_pj6, uarta, uartb, gmi, spi4),
GMUX(0x170, J, 5, uart2_cts_n_pj5, uarta, uartb, gmi, spi4),
GMUX(0x174, W, 6, uart3_txd_pw6, uartc, rsvd2, gmi, spi4),
GMUX(0x178, W, 7, uart3_rxd_pw7, uartc, rsvd2, gmi, spi4),
GMUX(0x17C, S, 1, uart3_cts_n_pa1, uartc, sdmmc1, dtv, gmi),
GMUX(0x180, C, 0, uart3_rts_n_pc0, uartc, pwm0, dtv, gmi),
GMUX(0x184, U, 0, pu0, owr, uarta, gmi, rsvd4),
GMUX(0x188, U, 1, pu1, rsvd1, uarta, gmi, rsvd4),
GMUX(0x18C, U, 2, pu2, rsvd1, uarta, gmi, rsvd4),
GMUX(0x190, U, 3, pu3, pwm0, uarta, gmi, displayb),
GMUX(0x194, U, 4, pu4, pwm1, uarta, gmi, displayb),
GMUX(0x198, U, 5, pu5, pwm2, uarta, gmi, displayb),
GMUX(0x19C, U, 6, pu6, pwm3, uarta, rsvd3, gmi),
GMUX(0x1A0, C, 5, gen1_i2c_sda_pc5, i2c1, rsvd2, rsvd3, rsvd4),
GMUX(0x1A4, C, 4, gen1_i2c_scl_pc4, i2c1, rsvd2, rsvd3, rsvd4),
GMUX(0x1A8, P, 3, dap4_fs_pp4, i2s3, gmi, dtv, rsvd4),
GMUX(0x1AC, P, 4, dap4_din_pp5, i2s3, gmi, rsvd3, rsvd4),
GMUX(0x1B0, P, 5, dap4_dout_pp6, i2s3, gmi, dtv, rsvd4),
GMUX(0x1B4, P, 7, dap4_sclk_pp7, i2s3, gmi, rsvd3, rsvd4),
GMUX(0x1B8, P, 0, clk3_out_pee0, extperiph3, rsvd2, rsvd3, rsvd4),
GMUX(0x1BC, EE, 1, clk3_req_pee1, dev3, rsvd2, rsvd3, rsvd4),
GMUX(0x1C0, C, 7, pc7, rsvd1, rsvd2, gmi, gmi_alt),
GMUX(0x1C4, I, 5, pi5, sdmmc2, rsvd2, gmi, rsvd4),
GMUX(0x1C8, I, 7, pi7, rsvd1, trace, gmi, dtv),
GMUX(0x1CC, K, 0, pk0, rsvd1, sdmmc3, gmi, soc),
GMUX(0x1D0, K, 1, pk1, sdmmc2, trace, gmi, rsvd4),
GMUX(0x1D4, J, 0, pj0, rsvd1, rsvd2, gmi, usb),
GMUX(0x1D8, J, 2, pj2, rsvd1, rsvd2, gmi, soc),
GMUX(0x1DC, K, 3, pk3, sdmmc2, trace, gmi, ccla),
GMUX(0x1E0, K, 4, pk4, sdmmc2, rsvd2, gmi, gmi_alt),
GMUX(0x1E4, K, 2, pk2, rsvd1, rsvd2, gmi, rsvd4),
GMUX(0x1E8, I, 3, pi3, rsvd1, rsvd2, gmi, spi4),
GMUX(0x1EC, I, 6, pi6, rsvd1, rsvd2, gmi, sdmmc2),
GMUX(0x1F0, G, 0, pg0, rsvd1, rsvd2, gmi, rsvd4),
GMUX(0x1F4, G, 1, pg1, rsvd1, rsvd2, gmi, rsvd4),
GMUX(0x1F8, G, 2, pg2, rsvd1, trace, gmi, rsvd4),
GMUX(0x1FC, G, 3, pg3, rsvd1, trace, gmi, rsvd4),
GMUX(0x200, G, 4, pg4, rsvd1, tmds, gmi, spi4),
GMUX(0x204, G, 5, pg5, rsvd1, rsvd2, gmi, spi4),
GMUX(0x208, G, 6, pg6, rsvd1, rsvd2, gmi, spi4),
GMUX(0x20C, G, 7, pg7, rsvd1, rsvd2, gmi, spi4),
GMUX(0x210, H, 0, ph0, pwm0, trace, gmi, dtv),
GMUX(0x214, H, 1, ph1, pwm1, tmds, gmi, displaya),
GMUX(0x218, H, 2, ph2, pwm2, tmds, gmi, cldvfs),
GMUX(0x21C, H, 3, ph3, pwm3, spi4, gmi, cldvfs),
GMUX(0x220, H, 4, ph4, sdmmc2, rsvd2, gmi, rsvd4),
GMUX(0x224, H, 5, ph5, sdmmc2, rsvd2, gmi, rsvd4),
GMUX(0x228, H, 6, ph6, sdmmc2, trace, gmi, dtv),
GMUX(0x22C, H, 7, ph7, sdmmc2, trace, gmi, dtv),
GMUX(0x230, J, 7, pj7, uartd, rsvd2, gmi, gmi_alt),
GMUX(0x234, B, 0, pb0, uartd, rsvd2, gmi, rsvd4),
GMUX(0x238, B, 1, pb1, uartd, rsvd2, gmi, rsvd4),
GMUX(0x23C, K, 7, pk7, uartd, rsvd2, gmi, rsvd4),
GMUX(0x240, I, 0, pi0, rsvd1, rsvd2, gmi, rsvd4),
GMUX(0x244, I, 1, pi1, rsvd1, rsvd2, gmi, rsvd4),
GMUX(0x248, I, 2, pi2, sdmmc2, trace, gmi, rsvd4),
GMUX(0x24C, I, 4, pi4, spi4, trace, gmi, displaya),
GMUX(0x250, T, 5, gen2_i2c_scl_pt5, i2c2, rsvd2, gmi, rsvd4),
GMUX(0x254, T, 6, gen2_i2c_sda_pt6, i2c2, rsvd2, gmi, rsvd4),
GMUX(0x258, CC, 4, sdmmc4_clk_pcc4, sdmmc4, rsvd2, gmi, rsvd4),
GMUX(0x25C, T, 7, sdmmc4_cmd_pt7, sdmmc4, rsvd2, gmi, rsvd4),
GMUX(0x260, AA, 0, sdmmc4_dat0_paa0, sdmmc4, spi3, gmi, rsvd4),
GMUX(0x264, AA, 1, sdmmc4_dat1_paa1, sdmmc4, spi3, gmi, rsvd4),
GMUX(0x268, AA, 2, sdmmc4_dat2_paa2, sdmmc4, spi3, gmi, rsvd4),
GMUX(0x26C, AA, 3, sdmmc4_dat3_paa3, sdmmc4, spi3, gmi, rsvd4),
GMUX(0x270, AA, 4, sdmmc4_dat4_paa4, sdmmc4, spi3, gmi, rsvd4),
GMUX(0x274, AA, 5, sdmmc4_dat5_paa5, sdmmc4, spi3, rsvd3, rsvd4),
GMUX(0x278, AA, 6, sdmmc4_dat6_paa6, sdmmc4, spi3, gmi, rsvd4),
GMUX(0x27C, AA, 7, sdmmc4_dat7_paa7, sdmmc4, rsvd2, gmi, rsvd4),
GMUX(0x284, CC, 0, cam_mclk_pcc0, vi, vi_alt1, vi_alt3, sdmmc2),
GMUX(0x288, CC, 1, pcc1, i2s4, rsvd2, rsvd3, sdmmc2),
GMUX(0x28C, BB, 0, pbb0, vgp6, vimclk2, sdmmc2, vimclk2_alt),
GMUX(0x290, BB, 1, cam_i2c_scl_pbb1, vgp1, i2c3, rsvd3, sdmmc2),
GMUX(0x294, BB, 2, cam_i2c_sda_pbb2, vgp2, i2c3, rsvd3, sdmmc2),
GMUX(0x298, BB, 3, pbb3, vgp3, displaya, displayb, sdmmc2),
GMUX(0x29C, BB, 4, pbb4, vgp4, displaya, displayb, sdmmc2),
GMUX(0x2A0, BB, 5, pbb5, vgp5, displaya, rsvd3, sdmmc2),
GMUX(0x2A4, BB, 6, pbb6, i2s4, rsvd2, displayb, sdmmc2),
GMUX(0x2A8, BB, 7, pbb7, i2s4, rsvd2, rsvd3, sdmmc2),
GMUX(0x2AC, CC, 2, pcc2, i2s4, rsvd2, sdmmc3, sdmmc2),
FMUX(0x2B0, jtag_rtck, rtck, rsvd2, rsvd3, rsvd4),
GMUX(0x2B4, Z, 6, pwr_i2c_scl_pz6, i2cpwr, rsvd2, rsvd3, rsvd4),
GMUX(0x2B8, Z, 7, pwr_i2c_sda_pz7, i2cpwr, rsvd2, rsvd3, rsvd4),
GMUX(0x2BC, R, 0, kb_row0_pr0, kbc, rsvd2, rsvd3, rsvd4),
GMUX(0x2C0, R, 1, kb_row1_pr1, kbc, rsvd2, rsvd3, rsvd4),
GMUX(0x2C4, R, 2, kb_row2_pr2, kbc, rsvd2, rsvd3, rsvd4),
GMUX(0x2C8, R, 3, kb_row3_pr3, kbc, displaya, sys, displayb),
GMUX(0x2CC, R, 4, kb_row4_pr4, kbc, displaya, rsvd3, displayb),
GMUX(0x2D0, R, 5, kb_row5_pr5, kbc, displaya, rsvd3, displayb),
GMUX(0x2D4, R, 6, kb_row6_pr6, kbc, displaya, displaya_alt, displayb),
GMUX(0x2D8, R, 7, kb_row7_pr7, kbc, rsvd2, cldvfs, uarta),
GMUX(0x2DC, S, 0, kb_row8_ps0, kbc, rsvd2, cldvfs, uarta),
GMUX(0x2E0, S, 1, kb_row9_ps1, kbc, rsvd2, rsvd3, uarta),
GMUX(0x2E4, S, 2, kb_row10_ps2, kbc, rsvd2, rsvd3, uarta),
GMUX(0x2E8, S, 3, kb_row11_ps3, kbc, rsvd2, rsvd3, irda),
GMUX(0x2EC, S, 4, kb_row12_ps4, kbc, rsvd2, rsvd3, irda),
GMUX(0x2F0, S, 5, kb_row13_ps5, kbc, rsvd2, spi2, rsvd4),
GMUX(0x2F4, S, 6, kb_row14_ps6, kbc, rsvd2, spi2, rsvd4),
GMUX(0x2F8, S, 7, kb_row15_ps7, kbc, soc, rsvd3, rsvd4),
GMUX(0x2FC, Q, 0, kb_col0_pq0, kbc, rsvd2, spi2, rsvd4),
GMUX(0x300, Q, 1, kb_col1_pq1, kbc, rsvd2, spi2, rsvd4),
GMUX(0x304, Q, 2, kb_col2_pq2, kbc, rsvd2, spi2, rsvd4),
GMUX(0x308, Q, 3, kb_col3_pq3, kbc, displaya, pwm2, uarta),
GMUX(0x30C, Q, 4, kb_col4_pq4, kbc, owr, sdmmc3, uarta),
GMUX(0x310, Q, 5, kb_col5_pq5, kbc, rsvd2, sdmmc3, rsvd4),
GMUX(0x314, Q, 6, kb_col6_pq6, kbc, rsvd2, spi2, uartd),
GMUX(0x318, Q, 7, kb_col7_pq7, kbc, rsvd2, spi2, uartd),
GMUX(0x31C, A, 0, clk_32k_out_pa0, blink, soc, rsvd3, rsvd4),
FMUX(0x324, core_pwr_req, pwron, rsvd2, rsvd3, rsvd4),
FMUX(0x328, cpu_pwr_req, cpu, rsvd2, rsvd3, rsvd4),
FMUX(0x32C, pwr_int_n, pmi, rsvd2, rsvd3, rsvd4),
FMUX(0x330, clk_32k_in, clk, rsvd2, rsvd3, rsvd4),
FMUX(0x334, owr, owr, rsvd2, rsvd3, rsvd4),
GMUX(0x338, N, 0, dap1_fs_pn0, i2s0, hda, gmi, rsvd4),
GMUX(0x33C, N, 1, dap1_din_pn1, i2s0, hda, gmi, rsvd4),
GMUX(0x340, N, 2, dap1_dout_pn2, i2s0, hda, gmi, sata),
GMUX(0x344, N, 3, dap1_sclk_pn3, i2s0, hda, gmi, rsvd4),
GMUX(0x348, EE, 2, dap_mclk1_req_pee2, dap, dap1, sata, rsvd4),
GMUX(0x34C, W, 4, dap_mclk1_pw4, extperiph1, dap2, rsvd3, rsvd4),
GMUX(0x350, K, 6, spdif_in_pk6, spdif, rsvd2, rsvd3, i2c3),
GMUX(0x354, K, 5, spdif_out_pk5, spdif, rsvd2, rsvd3, i2c3),
GMUX(0x358, A, 2, dap2_fs_pa2, i2s1, hda, gmi, rsvd4),
GMUX(0x35C, A, 4, dap2_din_pa4, i2s1, hda, gmi, rsvd4),
GMUX(0x360, A, 5, dap2_dout_pa5, i2s1, hda, gmi, rsvd4),
GMUX(0x364, A, 3, dap2_sclk_pa3, i2s1, hda, gmi, rsvd4),
GMUX(0x368, X, 0, dvfs_pwm_px0, spi6, cldvfs, gmi, rsvd4),
GMUX(0x36C, X, 1, gpio_x1_aud_px1, spi6, rsvd2, gmi, rsvd4),
GMUX(0x370, X, 3, gpio_x3_aud_px3, spi6, spi1, gmi, rsvd4),
GMUX(0x374, X, 2, dvfs_clk_px2, spi6, cldvfs, gmi, rsvd4),
GMUX(0x378, X, 4, gpio_x4_aud_px4, gmi, spi1, spi2, dap2),
GMUX(0x37C, X, 5, gpio_x5_aud_px5, gmi, spi1, spi2, rsvd4),
GMUX(0x380, X, 6, gpio_x6_aud_px6, spi6, spi1, spi2, gmi),
GMUX(0x384, X, 7, gpio_x7_aud_px7, rsvd1, spi1, spi2, rsvd4),
GMUX(0x390, A, 6, sdmmc3_clk_pa6, sdmmc3, rsvd2, rsvd3, spi3),
GMUX(0x394, A, 7, sdmmc3_cmd_pa7, sdmmc3, pwm3, uarta, spi3),
GMUX(0x398, B, 7, sdmmc3_dat0_pb7, sdmmc3, rsvd2, rsvd3, spi3),
GMUX(0x39C, B, 6, sdmmc3_dat1_pb6, sdmmc3, pwm2, uarta, spi3),
GMUX(0x3A0, B, 5, sdmmc3_dat2_pb5, sdmmc3, pwm1, displaya, spi3),
GMUX(0x3A4, B, 4, sdmmc3_dat3_pb4, sdmmc3, pwm0, displayb, spi3),
GMUX(0x3BC, DD, 1, pex_l0_rst_n_pdd1, pe0, rsvd2, rsvd3, rsvd4),
GMUX(0x3C0, DD, 2, pex_l0_clkreq_n_pdd2, pe0, rsvd2, rsvd3, rsvd4),
GMUX(0x3C4, DD, 3, pex_wake_n_pdd3, pe, rsvd2, rsvd3, rsvd4),
GMUX(0x3CC, DD, 5, pex_l1_rst_n_pdd5, pe1, rsvd2, rsvd3, rsvd4),
GMUX(0x3D0, DD, 6, pex_l1_clkreq_n_pdd6, pe1, rsvd2, rsvd3, rsvd4),
GMUX(0x3E0, EE, 3, hdmi_cec_pee3, cec, rsvd2, rsvd3, rsvd4),
GMUX(0x3E4, V, 3, sdmmc1_wp_n_pv3, sdmmc1, clk12, spi4, uarta),
GMUX(0x3E8, V, 2, sdmmc3_cd_n_pv2, sdmmc3, owr, rsvd3, rsvd4),
GMUX(0x3EC, W, 2, gpio_w2_aud_pw2, spi6, rsvd2, spi2, i2c1),
GMUX(0x3F0, W, 3, gpio_w3_aud_pw3, spi6, spi1, spi2, i2c1),
GMUX(0x3F4, N, 4, usb_vbus_en0_pn4, usb, rsvd2, rsvd3, rsvd4),
GMUX(0x3F8, N, 5, usb_vbus_en1_pn5, usb, rsvd2, rsvd3, rsvd4),
GMUX(0x3FC, EE, 5, sdmmc3_clk_lb_in_pee5, sdmmc3, rsvd2, rsvd3, rsvd4),
GMUX(0x400, EE, 4, sdmmc3_clk_lb_out_pee4, sdmmc3, rsvd2, rsvd3, rsvd4),
FMUX(0x404, gmi_clk_lb, sdmmc2, rsvd2, gmi, rsvd4),
FMUX(0x408, reset_out_n, rsvd1, rsvd2, rsvd3, reset_out_n),
GMUX(0x40C, T, 0, kb_row16_pt0, kbc, rsvd2, rsvd3, uartc),
GMUX(0x410, T, 1, kb_row17_pt1, kbc, rsvd2, rsvd3, uartc),
GMUX(0x414, FF, 1, usb_vbus_en2_pff1, usb, rsvd2, rsvd3, rsvd4),
GMUX(0x418, FF, 2, pff2, sata, rsvd2, rsvd3, rsvd4),
GMUX(0x430, FF, 0, dp_hpd_pff0, dp, rsvd2, rsvd3, rsvd4),
};
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 - 0x868, \
.drvdn_shift = dn_s, \
.drvdn_mask = (1 << dn_w) - 1, \
.drvup_shift = up_s, \
.drvup_mask = (1 << dn_w) - 1, \
}
/* Use register offsets from TRM */
static const struct tegra_grp pin_grp_tbl[] = {
GRP(0x868, ao1, 12, 5, 20, 5),
GRP(0x86C, ao2, 12, 5, 20, 5),
GRP(0x870, at1, 12, 7, 20, 7),
GRP(0x874, at2, 12, 7, 20, 7),
GRP(0x878, at3, 12, 7, 20, 7),
GRP(0x87C, at4, 12, 7, 20, 7),
GRP(0x880, at5, 14, 5, 19, 5),
GRP(0x884, cdev1, 12, 5, 20, 5),
GRP(0x888, cdev2, 12, 5, 20, 5),
GRP(0x890, dap1, 12, 5, 20, 5),
GRP(0x894, dap2, 12, 5, 20, 5),
GRP(0x898, dap3, 12, 5, 20, 5),
GRP(0x89C, dap4, 12, 5, 20, 5),
GRP(0x8A0, dbg, 12, 5, 20, 5),
GRP(0x8B0, sdio3, 12, 7, 20, 7),
GRP(0x8B4, spi, 12, 5, 20, 5),
GRP(0x8B8, uaa, 12, 5, 20, 5),
GRP(0x8BC, uab, 12, 5, 20, 5),
GRP(0x8C0, uart2, 12, 5, 20, 5),
GRP(0x8C4, uart3, 12, 5, 20, 5),
GRP(0x8EC, sdio1, 12, 7, 20, 7),
GRP(0x8FC, ddc, 12, 5, 20, 5),
GRP(0x900, gma, 14, 5, 20, 5),
GRP(0x910, gme, 14, 5, 19, 5),
GRP(0x914, gmf, 14, 5, 19, 5),
GRP(0x918, gmg, 14, 5, 19, 5),
GRP(0x91C, gmh, 14, 5, 19, 5),
GRP(0x920, owr, 12, 5, 20, 5),
GRP(0x924, uda, 12, 5, 20, 5),
GRP(0x928, gpv, 12, 5, 20, 5),
GRP(0x92C, dev3, 12, 5, 20, 5),
GRP(0x938, cec, 12, 5, 20, 5),
GRP(0x994, at6, 12, 7, 20, 7),
GRP(0x998, dap5, 12, 5, 20, 5),
GRP(0x99C, usb_vbus_en, 12, 5, 20, 5),
GRP(0x9A8, ao3, 12, 5, -1, 0),
GRP(0x9B0, ao0, 12, 5, 20, 5),
GRP(0x9B4, hv0, 12, 5, -1, 0),
GRP(0x9C4, sdio4, 12, 5, 20, 5),
GRP(0x9C8, ao4, 12, 7, 20, 7),
};
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 != -1)) {
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 != -1)) {
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;
uint32_t reg;
int rv;
/* Handle MIPI special case first */
if (strcmp(pin_name, "dsi_b") == 0) {
if (cfg->function == NULL) {
/* nothing to set */
return (0);
}
reg = bus_read_4(sc->mipi_mem_res, 0); /* register 0x820 */
if (strcmp(cfg->function, "csi") == 0)
reg &= ~(1 << 1);
else if (strcmp(cfg->function, "dsi_b") == 0)
reg |= (1 << 1);
bus_write_4(sc->mipi_mem_res, 0, reg); /* register 0x820 */
}
/* Handle pin muxes */
mux = pinmux_search_mux(pin_name);
if (mux != NULL) {
if (mux->gpio_num != -1) {
/* XXXX TODO: Reserve gpio here */
}
rv = pinmux_config_mux(sc, pin_name, mux, cfg);
return (rv);
}
/* Handle pin groups */
grp = pinmux_search_grp(pin_name);
if (grp != NULL) {
rv = pinmux_config_grp(sc, pin_name, grp, cfg);
return (rv);
}
device_printf(sc->dev, "Unknown pin: %s\n", pin_name);
return (ENXIO);
}
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", 1, (void **)pins);
if (*lpins <= 0)
return (ENOENT);
/* Read function (mux) settings. */
rv = OF_getprop_alloc(node, "nvidia,function", 1,
(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;
int rv;
sc = device_get_softc(dev);
cfgnode = OF_node_from_xref(cfgxref);
for (node = OF_child(cfgnode); node != 0; node = OF_peer(node)) {
- if (!fdt_is_enabled(node))
+ if (!ofw_bus_node_status_okay(node))
continue;
rv = 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);
}
rid = 2;
sc->mipi_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
RF_ACTIVE);
if (sc->mipi_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 tegra_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 devclass_t tegra_pinmux_devclass;
static DEFINE_CLASS_0(pinmux, tegra_pinmux_driver, tegra_pinmux_methods,
sizeof(struct pinmux_softc));
EARLY_DRIVER_MODULE(tegra_pinmux, simplebus, tegra_pinmux_driver,
tegra_pinmux_devclass, NULL, NULL, 71);
Index: head/sys/dev/fdt/fdt_pinctrl.c
===================================================================
--- head/sys/dev/fdt/fdt_pinctrl.c (revision 331923)
+++ head/sys/dev/fdt/fdt_pinctrl.c (revision 331924)
@@ -1,150 +1,150 @@
/*-
* Copyright (c) 2014 Ian Lepore
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* $FreeBSD$
*/
#include
#include
#include
#include
#include "fdt_pinctrl_if.h"
#include
#include
int
fdt_pinctrl_configure(device_t client, u_int index)
{
device_t pinctrl;
phandle_t *configs;
int i, nconfigs;
char name[16];
snprintf(name, sizeof(name), "pinctrl-%u", index);
nconfigs = OF_getencprop_alloc(ofw_bus_get_node(client), name,
sizeof(*configs), (void **)&configs);
if (nconfigs < 0)
return (ENOENT);
if (nconfigs == 0)
return (0); /* Empty property is documented as valid. */
for (i = 0; i < nconfigs; i++) {
if ((pinctrl = OF_device_from_xref(configs[i])) != NULL)
FDT_PINCTRL_CONFIGURE(pinctrl, configs[i]);
}
OF_prop_free(configs);
return (0);
}
int
fdt_pinctrl_configure_by_name(device_t client, const char * name)
{
char * names;
int i, offset, nameslen;
nameslen = OF_getprop_alloc(ofw_bus_get_node(client), "pinctrl-names",
sizeof(*names), (void **)&names);
if (nameslen <= 0)
return (ENOENT);
for (i = 0, offset = 0; offset < nameslen; i++) {
if (strcmp(name, &names[offset]) == 0)
break;
offset += strlen(&names[offset]) + 1;
}
OF_prop_free(names);
if (offset < nameslen)
return (fdt_pinctrl_configure(client, i));
else
return (ENOENT);
}
static int
pinctrl_register_children(device_t pinctrl, phandle_t parent,
const char *pinprop)
{
phandle_t node;
/*
* Recursively descend from parent, looking for nodes that have the
* given property, and associate the pinctrl device_t with each one.
*/
for (node = OF_child(parent); node != 0; node = OF_peer(node)) {
pinctrl_register_children(pinctrl, node, pinprop);
if (pinprop == NULL || OF_hasprop(node, pinprop)) {
OF_device_register_xref(OF_xref_from_node(node),
pinctrl);
}
}
return (0);
}
int
fdt_pinctrl_register(device_t pinctrl, const char *pinprop)
{
phandle_t node;
node = ofw_bus_get_node(pinctrl);
OF_device_register_xref(OF_xref_from_node(node), pinctrl);
return (pinctrl_register_children(pinctrl, node, pinprop));
}
static int
pinctrl_configure_children(device_t pinctrl, phandle_t parent)
{
phandle_t node, *configs;
int i, nconfigs;
for (node = OF_child(parent); node != 0; node = OF_peer(node)) {
- if (!fdt_is_enabled(node))
+ if (!ofw_bus_node_status_okay(node))
continue;
pinctrl_configure_children(pinctrl, node);
nconfigs = OF_getencprop_alloc(node, "pinctrl-0",
sizeof(*configs), (void **)&configs);
if (nconfigs <= 0)
continue;
if (bootverbose) {
char name[32];
OF_getprop(node, "name", &name, sizeof(name));
printf("Processing %d pin-config node(s) in pinctrl-0 for %s\n",
nconfigs, name);
}
for (i = 0; i < nconfigs; i++) {
if (OF_device_from_xref(configs[i]) == pinctrl)
FDT_PINCTRL_CONFIGURE(pinctrl, configs[i]);
}
OF_prop_free(configs);
}
return (0);
}
int
fdt_pinctrl_configure_tree(device_t pinctrl)
{
return (pinctrl_configure_children(pinctrl, OF_peer(0)));
}