mirror of https://github.com/F-Stack/f-stack.git
604 lines
15 KiB
C
604 lines
15 KiB
C
|
/*-
|
||
|
* Copyright (c) 2016 Michal Meloun <mmel@FreeBSD.org>
|
||
|
* 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 <sys/param.h>
|
||
|
#include <sys/systm.h>
|
||
|
#include <sys/bus.h>
|
||
|
#include <sys/kernel.h>
|
||
|
#include <sys/module.h>
|
||
|
#include <sys/malloc.h>
|
||
|
#include <sys/rman.h>
|
||
|
|
||
|
#include <machine/bus.h>
|
||
|
#include <machine/fdt.h>
|
||
|
|
||
|
#include <dev/extres/hwreset/hwreset.h>
|
||
|
#include <dev/extres/phy/phy.h>
|
||
|
#include <dev/fdt/fdt_common.h>
|
||
|
#include <dev/fdt/fdt_pinctrl.h>
|
||
|
#include <dev/ofw/openfirm.h>
|
||
|
#include <dev/ofw/ofw_bus.h>
|
||
|
#include <dev/ofw/ofw_bus_subr.h>
|
||
|
|
||
|
#include <gnu/dts/include/dt-bindings/pinctrl/pinctrl-tegra-xusb.h>
|
||
|
|
||
|
#include "phy_if.h"
|
||
|
|
||
|
#define XUSB_PADCTL_USB2_PAD_MUX 0x004
|
||
|
|
||
|
#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 XUSB_PADCTL_IOPHY_PLL_P0_CTL1 0x040
|
||
|
#define IOPHY_PLL_P0_CTL1_PLL0_LOCKDET (1 << 19)
|
||
|
#define IOPHY_PLL_P0_CTL1_REFCLK_SEL_MASK (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_USB3_PAD_MUX 0x134
|
||
|
|
||
|
#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
|
||
|
|
||
|
struct lane_cfg {
|
||
|
char *function;
|
||
|
char **lanes;
|
||
|
int iddq;
|
||
|
};
|
||
|
|
||
|
struct xusbpadctl_softc {
|
||
|
device_t dev;
|
||
|
struct resource *mem_res;
|
||
|
hwreset_t rst;
|
||
|
int phy_ena_cnt;
|
||
|
};
|
||
|
|
||
|
static struct ofw_compat_data compat_data[] = {
|
||
|
{"nvidia,tegra124-xusb-padctl", 1},
|
||
|
{NULL, 0},
|
||
|
};
|
||
|
|
||
|
struct padctl_lane {
|
||
|
const char *name;
|
||
|
bus_size_t reg;
|
||
|
uint32_t shift;
|
||
|
uint32_t mask;
|
||
|
int iddq;
|
||
|
char **mux;
|
||
|
int nmux;
|
||
|
};
|
||
|
|
||
|
static char *otg_mux[] = {"snps", "xusb", "uart", "rsvd"};
|
||
|
static char *usb_mux[] = {"snps", "xusb"};
|
||
|
static char *pci_mux[] = {"pcie", "usb3", "sata", "rsvd"};
|
||
|
|
||
|
#define LANE(n, r, s, m, i, mx) \
|
||
|
{ \
|
||
|
.name = n, \
|
||
|
.reg = r, \
|
||
|
.shift = s, \
|
||
|
.mask = m, \
|
||
|
.iddq = i, \
|
||
|
.mux = mx, \
|
||
|
.nmux = nitems(mx), \
|
||
|
}
|
||
|
|
||
|
static const struct padctl_lane lanes_tbl[] = {
|
||
|
LANE("otg-0", XUSB_PADCTL_USB2_PAD_MUX, 0, 0x3, -1, otg_mux),
|
||
|
LANE("otg-1", XUSB_PADCTL_USB2_PAD_MUX, 2, 0x3, -1, otg_mux),
|
||
|
LANE("otg-2", XUSB_PADCTL_USB2_PAD_MUX, 4, 0x3, -1, otg_mux),
|
||
|
LANE("ulpi-0", XUSB_PADCTL_USB2_PAD_MUX, 12, 0x1, -1, usb_mux),
|
||
|
LANE("hsic-0", XUSB_PADCTL_USB2_PAD_MUX, 14, 0x1, -1, usb_mux),
|
||
|
LANE("hsic-1", XUSB_PADCTL_USB2_PAD_MUX, 15, 0x1, -1, usb_mux),
|
||
|
LANE("pcie-0", XUSB_PADCTL_USB3_PAD_MUX, 16, 0x3, 1, pci_mux),
|
||
|
LANE("pcie-1", XUSB_PADCTL_USB3_PAD_MUX, 18, 0x3, 2, pci_mux),
|
||
|
LANE("pcie-2", XUSB_PADCTL_USB3_PAD_MUX, 20, 0x3, 3, pci_mux),
|
||
|
LANE("pcie-3", XUSB_PADCTL_USB3_PAD_MUX, 22, 0x3, 4, pci_mux),
|
||
|
LANE("pcie-4", XUSB_PADCTL_USB3_PAD_MUX, 24, 0x3, 5, pci_mux),
|
||
|
LANE("sata-0", XUSB_PADCTL_USB3_PAD_MUX, 26, 0x3, 6, pci_mux),
|
||
|
};
|
||
|
|
||
|
static int
|
||
|
xusbpadctl_mux_function(const 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
|
||
|
xusbpadctl_config_lane(struct xusbpadctl_softc *sc, char *lane_name,
|
||
|
const struct padctl_lane *lane, struct lane_cfg *cfg)
|
||
|
{
|
||
|
|
||
|
int tmp;
|
||
|
uint32_t reg;
|
||
|
|
||
|
reg = bus_read_4(sc->mem_res, lane->reg);
|
||
|
if (cfg->function != NULL) {
|
||
|
tmp = xusbpadctl_mux_function(lane, cfg->function);
|
||
|
if (tmp == -1) {
|
||
|
device_printf(sc->dev,
|
||
|
"Unknown function %s for lane %s\n", cfg->function,
|
||
|
lane_name);
|
||
|
return (EINVAL);
|
||
|
}
|
||
|
reg &= ~(lane->mask << lane->shift);
|
||
|
reg |= (tmp & lane->mask) << lane->shift;
|
||
|
}
|
||
|
if (cfg->iddq != -1) {
|
||
|
if (lane->iddq == -1) {
|
||
|
device_printf(sc->dev, "Invalid IDDQ for lane %s\n",
|
||
|
lane_name);
|
||
|
return (EINVAL);
|
||
|
}
|
||
|
if (cfg->iddq != 0)
|
||
|
reg &= ~(1 << lane->iddq);
|
||
|
else
|
||
|
reg |= 1 << lane->iddq;
|
||
|
}
|
||
|
|
||
|
bus_write_4(sc->mem_res, lane->reg, reg);
|
||
|
return (0);
|
||
|
}
|
||
|
|
||
|
static const struct padctl_lane *
|
||
|
xusbpadctl_search_lane(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 int
|
||
|
xusbpadctl_config_node(struct xusbpadctl_softc *sc, char *lane_name,
|
||
|
struct lane_cfg *cfg)
|
||
|
{
|
||
|
const struct padctl_lane *lane;
|
||
|
int rv;
|
||
|
|
||
|
lane = xusbpadctl_search_lane(lane_name);
|
||
|
if (lane == NULL) {
|
||
|
device_printf(sc->dev, "Unknown lane: %s\n", lane_name);
|
||
|
return (ENXIO);
|
||
|
}
|
||
|
rv = xusbpadctl_config_lane(sc, lane_name, lane, cfg);
|
||
|
return (rv);
|
||
|
}
|
||
|
|
||
|
static int
|
||
|
xusbpadctl_read_node(struct xusbpadctl_softc *sc, phandle_t node,
|
||
|
struct lane_cfg *cfg, char **lanes, int *llanes)
|
||
|
{
|
||
|
int rv;
|
||
|
|
||
|
*llanes = OF_getprop_alloc(node, "nvidia,lanes", 1, (void **)lanes);
|
||
|
if (*llanes <= 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. */
|
||
|
rv = OF_getencprop(node, "nvidia,iddq", &cfg->iddq,
|
||
|
sizeof(cfg->iddq));
|
||
|
if (rv <= 0)
|
||
|
cfg->iddq = -1;
|
||
|
return (0);
|
||
|
}
|
||
|
|
||
|
static int
|
||
|
xusbpadctl_process_node(struct xusbpadctl_softc *sc, phandle_t node)
|
||
|
{
|
||
|
struct lane_cfg cfg;
|
||
|
char *lanes, *lname;
|
||
|
int i, len, llanes, rv;
|
||
|
|
||
|
rv = xusbpadctl_read_node(sc, node, &cfg, &lanes, &llanes);
|
||
|
if (rv != 0)
|
||
|
return (rv);
|
||
|
|
||
|
len = 0;
|
||
|
lname = lanes;
|
||
|
do {
|
||
|
i = strlen(lname) + 1;
|
||
|
rv = xusbpadctl_config_node(sc, lname, &cfg);
|
||
|
if (rv != 0)
|
||
|
device_printf(sc->dev,
|
||
|
"Cannot configure lane: %s: %d\n", lname, rv);
|
||
|
|
||
|
len += i;
|
||
|
lname += i;
|
||
|
} while (len < llanes);
|
||
|
|
||
|
if (lanes != NULL)
|
||
|
OF_prop_free(lanes);
|
||
|
if (cfg.function != NULL)
|
||
|
OF_prop_free(cfg.function);
|
||
|
return (rv);
|
||
|
}
|
||
|
|
||
|
|
||
|
static int
|
||
|
xusbpadctl_pinctrl_cfg(device_t dev, phandle_t cfgxref)
|
||
|
{
|
||
|
struct xusbpadctl_softc *sc;
|
||
|
phandle_t node, cfgnode;
|
||
|
int rv;
|
||
|
|
||
|
sc = device_get_softc(dev);
|
||
|
cfgnode = OF_node_from_xref(cfgxref);
|
||
|
|
||
|
rv = 0;
|
||
|
for (node = OF_child(cfgnode); node != 0; node = OF_peer(node)) {
|
||
|
if (!fdt_is_enabled(node))
|
||
|
continue;
|
||
|
rv = xusbpadctl_process_node(sc, node);
|
||
|
if (rv != 0)
|
||
|
return (rv);
|
||
|
}
|
||
|
|
||
|
return (rv);
|
||
|
}
|
||
|
|
||
|
static int
|
||
|
xusbpadctl_phy_pcie_powerup(struct xusbpadctl_softc *sc)
|
||
|
{
|
||
|
uint32_t reg;
|
||
|
int i;
|
||
|
|
||
|
reg = bus_read_4(sc->mem_res, XUSB_PADCTL_IOPHY_PLL_P0_CTL1);
|
||
|
reg &= ~IOPHY_PLL_P0_CTL1_REFCLK_SEL_MASK;
|
||
|
bus_write_4(sc->mem_res, XUSB_PADCTL_IOPHY_PLL_P0_CTL1, reg);
|
||
|
DELAY(100);
|
||
|
|
||
|
reg = bus_read_4(sc->mem_res, 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;
|
||
|
bus_write_4(sc->mem_res, XUSB_PADCTL_IOPHY_PLL_P0_CTL2, reg);
|
||
|
DELAY(100);
|
||
|
|
||
|
reg = bus_read_4(sc->mem_res, XUSB_PADCTL_IOPHY_PLL_P0_CTL1);
|
||
|
reg |= IOPHY_PLL_P0_CTL1_PLL_RST;
|
||
|
bus_write_4(sc->mem_res, XUSB_PADCTL_IOPHY_PLL_P0_CTL1, reg);
|
||
|
DELAY(100);
|
||
|
|
||
|
for (i = 0; i < 100; i++) {
|
||
|
reg = bus_read_4(sc->mem_res, XUSB_PADCTL_IOPHY_PLL_P0_CTL1);
|
||
|
if (reg & IOPHY_PLL_P0_CTL1_PLL0_LOCKDET)
|
||
|
return (0);
|
||
|
DELAY(10);
|
||
|
}
|
||
|
|
||
|
return (ETIMEDOUT);
|
||
|
}
|
||
|
|
||
|
|
||
|
static int
|
||
|
xusbpadctl_phy_pcie_powerdown(struct xusbpadctl_softc *sc)
|
||
|
{
|
||
|
uint32_t reg;
|
||
|
|
||
|
reg = bus_read_4(sc->mem_res, XUSB_PADCTL_IOPHY_PLL_P0_CTL1);
|
||
|
reg &= ~IOPHY_PLL_P0_CTL1_PLL_RST;
|
||
|
bus_write_4(sc->mem_res, XUSB_PADCTL_IOPHY_PLL_P0_CTL1, reg);
|
||
|
DELAY(100);
|
||
|
return (0);
|
||
|
|
||
|
}
|
||
|
|
||
|
static int
|
||
|
xusbpadctl_phy_sata_powerup(struct xusbpadctl_softc *sc)
|
||
|
{
|
||
|
uint32_t reg;
|
||
|
int i;
|
||
|
|
||
|
reg = bus_read_4(sc->mem_res, XUSB_PADCTL_IOPHY_MISC_PAD_S0_CTL1);
|
||
|
reg &= ~IOPHY_MISC_PAD_S0_CTL1_IDDQ_OVRD;
|
||
|
reg &= ~IOPHY_MISC_PAD_S0_CTL1_IDDQ;
|
||
|
bus_write_4(sc->mem_res, XUSB_PADCTL_IOPHY_MISC_PAD_S0_CTL1, reg);
|
||
|
|
||
|
reg = bus_read_4(sc->mem_res, XUSB_PADCTL_IOPHY_PLL_S0_CTL1);
|
||
|
reg &= ~IOPHY_PLL_S0_CTL1_PLL_PWR_OVRD;
|
||
|
reg &= ~IOPHY_PLL_S0_CTL1_PLL_IDDQ;
|
||
|
bus_write_4(sc->mem_res, XUSB_PADCTL_IOPHY_PLL_S0_CTL1, reg);
|
||
|
|
||
|
reg = bus_read_4(sc->mem_res, XUSB_PADCTL_IOPHY_PLL_S0_CTL1);
|
||
|
reg |= IOPHY_PLL_S0_CTL1_PLL1_MODE;
|
||
|
bus_write_4(sc->mem_res, XUSB_PADCTL_IOPHY_PLL_S0_CTL1, reg);
|
||
|
|
||
|
reg = bus_read_4(sc->mem_res, XUSB_PADCTL_IOPHY_PLL_S0_CTL1);
|
||
|
reg |= IOPHY_PLL_S0_CTL1_PLL_RST_L;
|
||
|
bus_write_4(sc->mem_res, XUSB_PADCTL_IOPHY_PLL_S0_CTL1, reg);
|
||
|
|
||
|
for (i = 100; i >= 0; i--) {
|
||
|
reg = bus_read_4(sc->mem_res, 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);
|
||
|
}
|
||
|
|
||
|
return (0);
|
||
|
}
|
||
|
|
||
|
static int
|
||
|
xusbpadctl_phy_sata_powerdown(struct xusbpadctl_softc *sc)
|
||
|
{
|
||
|
uint32_t reg;
|
||
|
|
||
|
reg = bus_read_4(sc->mem_res, XUSB_PADCTL_IOPHY_PLL_S0_CTL1);
|
||
|
reg &= ~IOPHY_PLL_S0_CTL1_PLL_RST_L;
|
||
|
bus_write_4(sc->mem_res, XUSB_PADCTL_IOPHY_PLL_S0_CTL1, reg);
|
||
|
DELAY(100);
|
||
|
|
||
|
reg = bus_read_4(sc->mem_res, XUSB_PADCTL_IOPHY_PLL_S0_CTL1);
|
||
|
reg &= ~IOPHY_PLL_S0_CTL1_PLL1_MODE;
|
||
|
bus_write_4(sc->mem_res, XUSB_PADCTL_IOPHY_PLL_S0_CTL1, reg);
|
||
|
DELAY(100);
|
||
|
|
||
|
reg = bus_read_4(sc->mem_res, XUSB_PADCTL_IOPHY_PLL_S0_CTL1);
|
||
|
reg |= IOPHY_PLL_S0_CTL1_PLL_PWR_OVRD;
|
||
|
reg |= IOPHY_PLL_S0_CTL1_PLL_IDDQ;
|
||
|
bus_write_4(sc->mem_res, XUSB_PADCTL_IOPHY_PLL_S0_CTL1, reg);
|
||
|
DELAY(100);
|
||
|
|
||
|
reg = bus_read_4(sc->mem_res, XUSB_PADCTL_IOPHY_MISC_PAD_S0_CTL1);
|
||
|
reg |= IOPHY_MISC_PAD_S0_CTL1_IDDQ_OVRD;
|
||
|
reg |= IOPHY_MISC_PAD_S0_CTL1_IDDQ;
|
||
|
bus_write_4(sc->mem_res, XUSB_PADCTL_IOPHY_MISC_PAD_S0_CTL1, reg);
|
||
|
DELAY(100);
|
||
|
|
||
|
return (0);
|
||
|
}
|
||
|
|
||
|
static int
|
||
|
xusbpadctl_phy_powerup(struct xusbpadctl_softc *sc)
|
||
|
{
|
||
|
uint32_t reg;
|
||
|
|
||
|
reg = bus_read_4(sc->mem_res, XUSB_PADCTL_ELPG_PROGRAM);
|
||
|
reg &= ~ELPG_PROGRAM_AUX_MUX_LP0_CLAMP_EN;
|
||
|
bus_write_4(sc->mem_res, XUSB_PADCTL_ELPG_PROGRAM, reg);
|
||
|
DELAY(100);
|
||
|
|
||
|
reg = bus_read_4(sc->mem_res, XUSB_PADCTL_ELPG_PROGRAM);
|
||
|
reg &= ~ELPG_PROGRAM_AUX_MUX_LP0_CLAMP_EN_EARLY;
|
||
|
bus_write_4(sc->mem_res, XUSB_PADCTL_ELPG_PROGRAM, reg);
|
||
|
DELAY(100);
|
||
|
|
||
|
reg = bus_read_4(sc->mem_res, XUSB_PADCTL_ELPG_PROGRAM);
|
||
|
reg &= ~ELPG_PROGRAM_AUX_MUX_LP0_VCORE_DOWN;
|
||
|
bus_write_4(sc->mem_res, XUSB_PADCTL_ELPG_PROGRAM, reg);
|
||
|
DELAY(100);
|
||
|
|
||
|
return (0);
|
||
|
}
|
||
|
|
||
|
static int
|
||
|
xusbpadctl_phy_powerdown(struct xusbpadctl_softc *sc)
|
||
|
{
|
||
|
uint32_t reg;
|
||
|
|
||
|
reg = bus_read_4(sc->mem_res, XUSB_PADCTL_ELPG_PROGRAM);
|
||
|
reg |= ELPG_PROGRAM_AUX_MUX_LP0_VCORE_DOWN;
|
||
|
bus_write_4(sc->mem_res, XUSB_PADCTL_ELPG_PROGRAM, reg);
|
||
|
DELAY(100);
|
||
|
|
||
|
reg = bus_read_4(sc->mem_res, XUSB_PADCTL_ELPG_PROGRAM);
|
||
|
reg |= ELPG_PROGRAM_AUX_MUX_LP0_CLAMP_EN_EARLY;
|
||
|
bus_write_4(sc->mem_res, XUSB_PADCTL_ELPG_PROGRAM, reg);
|
||
|
DELAY(100);
|
||
|
|
||
|
reg = bus_read_4(sc->mem_res, XUSB_PADCTL_ELPG_PROGRAM);
|
||
|
reg |= ELPG_PROGRAM_AUX_MUX_LP0_CLAMP_EN;
|
||
|
bus_write_4(sc->mem_res, XUSB_PADCTL_ELPG_PROGRAM, reg);
|
||
|
DELAY(100);
|
||
|
|
||
|
return (0);
|
||
|
}
|
||
|
|
||
|
static int
|
||
|
xusbpadctl_phy_enable(device_t dev, intptr_t id, bool enable)
|
||
|
{
|
||
|
struct xusbpadctl_softc *sc;
|
||
|
int rv;
|
||
|
|
||
|
sc = device_get_softc(dev);
|
||
|
|
||
|
if ((id != TEGRA_XUSB_PADCTL_PCIE) &&
|
||
|
(id != TEGRA_XUSB_PADCTL_SATA)) {
|
||
|
device_printf(dev, "Unknown phy: %d\n", id);
|
||
|
return (ENXIO);
|
||
|
}
|
||
|
|
||
|
rv = 0;
|
||
|
if (enable) {
|
||
|
if (sc->phy_ena_cnt == 0) {
|
||
|
rv = xusbpadctl_phy_powerup(sc);
|
||
|
if (rv != 0)
|
||
|
return (rv);
|
||
|
}
|
||
|
sc->phy_ena_cnt++;
|
||
|
}
|
||
|
|
||
|
if (id == TEGRA_XUSB_PADCTL_PCIE) {
|
||
|
if (enable)
|
||
|
rv = xusbpadctl_phy_pcie_powerup(sc);
|
||
|
else
|
||
|
rv = xusbpadctl_phy_pcie_powerdown(sc);
|
||
|
if (rv != 0)
|
||
|
return (rv);
|
||
|
} else if (id == TEGRA_XUSB_PADCTL_SATA) {
|
||
|
if (enable)
|
||
|
rv = xusbpadctl_phy_sata_powerup(sc);
|
||
|
else
|
||
|
rv = xusbpadctl_phy_sata_powerdown(sc);
|
||
|
if (rv != 0)
|
||
|
return (rv);
|
||
|
}
|
||
|
if (!enable) {
|
||
|
if (sc->phy_ena_cnt == 1) {
|
||
|
rv = xusbpadctl_phy_powerdown(sc);
|
||
|
if (rv != 0)
|
||
|
return (rv);
|
||
|
}
|
||
|
sc->phy_ena_cnt--;
|
||
|
}
|
||
|
|
||
|
return (0);
|
||
|
}
|
||
|
|
||
|
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 xusbpadctl_softc * sc;
|
||
|
int rid, rv;
|
||
|
phandle_t node;
|
||
|
|
||
|
sc = device_get_softc(dev);
|
||
|
sc->dev = 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);
|
||
|
}
|
||
|
|
||
|
node = ofw_bus_get_node(dev);
|
||
|
rv = hwreset_get_by_ofw_name(dev, "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);
|
||
|
}
|
||
|
|
||
|
/* Register as a pinctrl device and use default configuration */
|
||
|
fdt_pinctrl_register(dev, NULL);
|
||
|
fdt_pinctrl_configure_by_name(dev, "default");
|
||
|
phy_register_provider(dev);
|
||
|
|
||
|
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),
|
||
|
|
||
|
/* fdt_pinctrl interface */
|
||
|
DEVMETHOD(fdt_pinctrl_configure, xusbpadctl_pinctrl_cfg),
|
||
|
|
||
|
/* phy interface */
|
||
|
DEVMETHOD(phy_enable, xusbpadctl_phy_enable),
|
||
|
|
||
|
DEVMETHOD_END
|
||
|
};
|
||
|
|
||
|
static driver_t tegra_xusbpadctl_driver = {
|
||
|
"tegra_xusbpadctl",
|
||
|
tegra_xusbpadctl_methods,
|
||
|
sizeof(struct xusbpadctl_softc),
|
||
|
};
|
||
|
|
||
|
static devclass_t tegra_xusbpadctl_devclass;
|
||
|
|
||
|
EARLY_DRIVER_MODULE(tegra_xusbpadctl, simplebus, tegra_xusbpadctl_driver,
|
||
|
tegra_xusbpadctl_devclass, 0, 0, 73);
|