Source-Changes-HG archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

[src/trunk]: src/sys/arch/arm/nvidia Tegra USB PHY support



details:   https://anonhg.NetBSD.org/src/rev/6afbce1c0e86
branches:  trunk
changeset: 338073:6afbce1c0e86
user:      jmcneill <jmcneill%NetBSD.org@localhost>
date:      Sat May 09 18:56:51 2015 +0000

description:
Tegra USB PHY support

diffstat:

 sys/arch/arm/nvidia/tegra_car.c     |   92 +++++++++++++++-
 sys/arch/arm/nvidia/tegra_carreg.h  |   22 +++-
 sys/arch/arm/nvidia/tegra_ehci.c    |  204 ++++++++++++++++++++++++++++++++++-
 sys/arch/arm/nvidia/tegra_ehcireg.h |  113 +++++++++++++++++++
 sys/arch/arm/nvidia/tegra_var.h     |    5 +-
 5 files changed, 425 insertions(+), 11 deletions(-)

diffs (truncated from 545 to 300 lines):

diff -r 7377bdf7554b -r 6afbce1c0e86 sys/arch/arm/nvidia/tegra_car.c
--- a/sys/arch/arm/nvidia/tegra_car.c   Sat May 09 18:49:36 2015 +0000
+++ b/sys/arch/arm/nvidia/tegra_car.c   Sat May 09 18:56:51 2015 +0000
@@ -1,4 +1,4 @@
-/* $NetBSD: tegra_car.c,v 1.5 2015/05/09 11:17:59 jmcneill Exp $ */
+/* $NetBSD: tegra_car.c,v 1.6 2015/05/09 18:56:51 jmcneill Exp $ */
 
 /*-
  * Copyright (c) 2015 Jared D. McNeill <jmcneill%invisible.ca@localhost>
@@ -29,7 +29,7 @@
 #include "locators.h"
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: tegra_car.c,v 1.5 2015/05/09 11:17:59 jmcneill Exp $");
+__KERNEL_RCSID(0, "$NetBSD: tegra_car.c,v 1.6 2015/05/09 18:56:51 jmcneill Exp $");
 
 #include <sys/param.h>
 #include <sys/bus.h>
@@ -283,3 +283,91 @@
 
        return 0;
 }
+
+int
+tegra_car_periph_usb_enable(u_int port)
+{
+       bus_space_tag_t bst;
+       bus_space_handle_t bsh;
+       bus_size_t rst_reg, enb_reg;
+       uint32_t dev_bit;
+
+       tegra_car_get_bs(&bst, &bsh);
+       switch (port) {
+       case 0:
+               rst_reg = CAR_RST_DEV_L_SET_REG;
+               enb_reg = CAR_CLK_ENB_L_SET_REG;
+               dev_bit = CAR_DEV_L_USBD;
+               break;
+       case 1:
+               rst_reg = CAR_RST_DEV_H_SET_REG;
+               enb_reg = CAR_CLK_ENB_H_SET_REG;
+               dev_bit = CAR_DEV_H_USB2;
+               break;
+       case 2:
+               rst_reg = CAR_RST_DEV_H_SET_REG;
+               enb_reg = CAR_CLK_ENB_H_SET_REG;
+               dev_bit = CAR_DEV_H_USB3;
+               break;
+       default:
+               return EINVAL;
+       }
+
+       /* enter reset */
+       bus_space_write_4(bst, bsh, rst_reg, dev_bit);
+       /* enable clk */
+       bus_space_write_4(bst, bsh, enb_reg, dev_bit);
+
+       /* leave reset */
+       bus_space_write_4(bst, bsh, rst_reg+4, dev_bit);
+
+       return 0;
+}
+
+void
+tegra_car_utmip_init(void)
+{
+       const u_int enable_dly_count = 0x02;
+       const u_int stable_count = 0x33;
+       const u_int active_dly_count = 0x09;
+       const u_int xtal_freq_count = 0x7f;
+       bus_space_tag_t bst;
+       bus_space_handle_t bsh;
+
+       tegra_car_get_bs(&bst, &bsh);
+
+       tegra_reg_set_clear(bst, bsh, CAR_UTMIP_PLL_CFG2_REG,
+           __SHIFTIN(stable_count, CAR_UTMIP_PLL_CFG2_STABLE_COUNT) |
+           __SHIFTIN(active_dly_count, CAR_UTMIP_PLL_CFG2_ACTIVE_DLY_COUNT),
+           CAR_UTMIP_PLL_CFG2_STABLE_COUNT |
+           CAR_UTMIP_PLL_CFG2_ACTIVE_DLY_COUNT);
+
+       tegra_reg_set_clear(bst, bsh, CAR_UTMIP_PLL_CFG1_REG,
+           __SHIFTIN(enable_dly_count, CAR_UTMIP_PLL_CFG1_ENABLE_DLY_COUNT) |
+           __SHIFTIN(xtal_freq_count, CAR_UTMIP_PLL_CFG1_XTAL_FREQ_COUNT),
+           CAR_UTMIP_PLL_CFG1_ENABLE_DLY_COUNT |
+           CAR_UTMIP_PLL_CFG1_XTAL_FREQ_COUNT);
+
+       tegra_reg_set_clear(bst, bsh, CAR_UTMIP_PLL_CFG1_REG,
+           0,
+           CAR_UTMIP_PLL_CFG1_PLLU_POWERDOWN |
+           CAR_UTMIP_PLL_CFG1_PLL_ENABLE_POWERDOWN);
+}
+
+void
+tegra_car_utmip_enable(u_int port)
+{
+       bus_space_tag_t bst;
+       bus_space_handle_t bsh;
+       uint32_t bit = 0;
+
+       tegra_car_get_bs(&bst, &bsh);
+
+       switch (port) {
+       case 0: bit = CAR_UTMIP_PLL_CFG2_PD_SAMP_A_POWERDOWN; break;
+       case 1: bit = CAR_UTMIP_PLL_CFG2_PD_SAMP_B_POWERDOWN; break;
+       case 2: bit = CAR_UTMIP_PLL_CFG2_PD_SAMP_C_POWERDOWN; break;
+       }
+
+       tegra_reg_set_clear(bst, bsh, CAR_UTMIP_PLL_CFG2_REG, 0, bit);
+}
diff -r 7377bdf7554b -r 6afbce1c0e86 sys/arch/arm/nvidia/tegra_carreg.h
--- a/sys/arch/arm/nvidia/tegra_carreg.h        Sat May 09 18:49:36 2015 +0000
+++ b/sys/arch/arm/nvidia/tegra_carreg.h        Sat May 09 18:56:51 2015 +0000
@@ -1,4 +1,4 @@
-/* $NetBSD: tegra_carreg.h,v 1.6 2015/05/09 11:17:59 jmcneill Exp $ */
+/* $NetBSD: tegra_carreg.h,v 1.7 2015/05/09 18:56:51 jmcneill Exp $ */
 
 /*-
  * Copyright (c) 2015 Jared D. McNeill <jmcneill%invisible.ca@localhost>
@@ -213,4 +213,24 @@
 #define CAR_DEV_H_AHBDMA               __BIT(1)
 #define CAR_DEV_H_MEM                  __BIT(0)
 
+#define CAR_UTMIP_PLL_CFG0_REG         0x480
+
+#define CAR_UTMIP_PLL_CFG1_REG         0x484
+#define CAR_UTMIP_PLL_CFG1_ENABLE_DLY_COUNT    __BITS(31,27)
+#define CAR_UTMIP_PLL_CFG1_PLLU_POWERUP                __BIT(17)
+#define CAR_UTMIP_PLL_CFG1_PLLU_POWERDOWN      __BIT(16)
+#define CAR_UTMIP_PLL_CFG1_PLL_ENABLE_POWERUP  __BIT(15)
+#define CAR_UTMIP_PLL_CFG1_PLL_ENABLE_POWERDOWN        __BIT(14)
+#define CAR_UTMIP_PLL_CFG1_XTAL_FREQ_COUNT     __BITS(11,0)
+
+#define CAR_UTMIP_PLL_CFG2_REG         0x488
+#define CAR_UTMIP_PLL_CFG2_ACTIVE_DLY_COUNT    __BITS(23,18)
+#define CAR_UTMIP_PLL_CFG2_STABLE_COUNT                __BITS(17,6)
+#define CAR_UTMIP_PLL_CFG2_PD_SAMP_C_POWERUP   __BIT(5)
+#define CAR_UTMIP_PLL_CFG2_PD_SAMP_C_POWERDOWN __BIT(4)
+#define CAR_UTMIP_PLL_CFG2_PD_SAMP_B_POWERUP   __BIT(3)
+#define CAR_UTMIP_PLL_CFG2_PD_SAMP_B_POWERDOWN __BIT(2)
+#define CAR_UTMIP_PLL_CFG2_PD_SAMP_A_POWERUP   __BIT(1)
+#define CAR_UTMIP_PLL_CFG2_PD_SAMP_A_POWERDOWN __BIT(0)
+
 #endif /* _ARM_TEGRA_CARREG_H */
diff -r 7377bdf7554b -r 6afbce1c0e86 sys/arch/arm/nvidia/tegra_ehci.c
--- a/sys/arch/arm/nvidia/tegra_ehci.c  Sat May 09 18:49:36 2015 +0000
+++ b/sys/arch/arm/nvidia/tegra_ehci.c  Sat May 09 18:56:51 2015 +0000
@@ -1,4 +1,4 @@
-/* $NetBSD: tegra_ehci.c,v 1.3 2015/05/09 12:07:52 jmcneill Exp $ */
+/* $NetBSD: tegra_ehci.c,v 1.4 2015/05/09 18:56:51 jmcneill Exp $ */
 
 /*-
  * Copyright (c) 2015 Jared D. McNeill <jmcneill%invisible.ca@localhost>
@@ -29,7 +29,7 @@
 #include "locators.h"
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: tegra_ehci.c,v 1.3 2015/05/09 12:07:52 jmcneill Exp $");
+__KERNEL_RCSID(0, "$NetBSD: tegra_ehci.c,v 1.4 2015/05/09 18:56:51 jmcneill Exp $");
 
 #include <sys/param.h>
 #include <sys/bus.h>
@@ -46,19 +46,27 @@
 #include <dev/usb/ehcivar.h>
 
 #include <arm/nvidia/tegra_var.h>
+#include <arm/nvidia/tegra_ehcireg.h>
 
 #define TEGRA_EHCI_REG_OFFSET  0x100
 
 static int     tegra_ehci_match(device_t, cfdata_t, void *);
 static void    tegra_ehci_attach(device_t, device_t, void *);
 
+static void    tegra_ehci_init(struct ehci_softc *);
+
 struct tegra_ehci_softc {
        struct ehci_softc       sc;
+       bus_space_tag_t         sc_bst;
+       bus_space_handle_t      sc_bsh;
        void                    *sc_ih;
+       u_int                   sc_port;
 
        struct tegra_gpio_pin   *sc_pin_vbus;
 };
 
+static void    tegra_ehci_utmip_init(struct tegra_ehci_softc *);
+
 CFATTACH_DECL2_NEW(tegra_ehci, sizeof(struct tegra_ehci_softc),
        tegra_ehci_match, tegra_ehci_attach, NULL,
        ehci_activate, NULL, ehci_childdet);
@@ -79,11 +87,16 @@
        const char *pin;
        int error;
 
+       sc->sc_bst = tio->tio_bst;
+       bus_space_subregion(tio->tio_bst, tio->tio_bsh,
+           loc->loc_offset, loc->loc_size, &sc->sc_bsh);
+       sc->sc_port = loc->loc_port;
+
        sc->sc.sc_dev = self;
        sc->sc.sc_bus.hci_private = &sc->sc;
        sc->sc.sc_bus.dmatag = tio->tio_dmat;
        sc->sc.sc_bus.usbrev = USBREV_2_0;
-       sc->sc.sc_flags = EHCIF_ETTF;
+       sc->sc.sc_flags = 0;    /* XXX EHCIF_ETTF */
        sc->sc.sc_id_vendor = 0x10de;
        strlcpy(sc->sc.sc_vendor, "Tegra", sizeof(sc->sc.sc_vendor));
        sc->sc.sc_size = loc->loc_size;
@@ -91,15 +104,27 @@
        bus_space_subregion(tio->tio_bst, tio->tio_bsh,
            loc->loc_offset + TEGRA_EHCI_REG_OFFSET,
            loc->loc_size - TEGRA_EHCI_REG_OFFSET, &sc->sc.ioh);
+       sc->sc.sc_vendor_init = tegra_ehci_init;
 
        aprint_naive("\n");
        aprint_normal(": USB%d\n", loc->loc_port + 1);
 
+       tegra_car_periph_usb_enable(sc->sc_port);
+       delay(2);
+
+       tegra_ehci_utmip_init(sc);
+
        if (prop_dictionary_get_cstring_nocopy(prop, "vbus-gpio", &pin)) {
-               sc->sc_pin_vbus = tegra_gpio_acquire(pin,
-                   GPIO_PIN_OUTPUT | GPIO_PIN_OPENDRAIN);
-               if (sc->sc_pin_vbus)
-                       tegra_gpio_write(sc->sc_pin_vbus, 1);
+               const uint32_t v = bus_space_read_4(sc->sc_bst, sc->sc_bsh,
+                   TEGRA_EHCI_PHY_VBUS_SENSORS_REG);
+               if ((v & TEGRA_EHCI_PHY_VBUS_SENSORS_A_VBUS_VLD_STS) == 0) {
+                       sc->sc_pin_vbus = tegra_gpio_acquire(pin,
+                           GPIO_PIN_OUTPUT | GPIO_PIN_OPENDRAIN);
+                       if (sc->sc_pin_vbus)
+                               tegra_gpio_write(sc->sc_pin_vbus, 1);
+               } else {
+                       aprint_normal_dev(self, "VBUS input active\n");
+               }
         }
 
        sc->sc.sc_offs = EREAD1(&sc->sc, EHCI_CAPLENGTH);
@@ -121,3 +146,168 @@
 
        sc->sc.sc_child = config_found(self, &sc->sc.sc_bus, usbctlprint);
 }
+
+static void
+tegra_ehci_init(struct ehci_softc *esc)
+{
+       struct tegra_ehci_softc * const sc = device_private(esc->sc_dev);
+       uint32_t usbmode;
+
+       usbmode = bus_space_read_4(sc->sc_bst, sc->sc_bsh,
+           TEGRA_EHCI_USBMODE_REG);
+
+       const u_int cm = __SHIFTOUT(usbmode, TEGRA_EHCI_USBMODE_CM);
+       if (cm != TEGRA_EHCI_USBMODE_CM_HOST) {
+               aprint_verbose_dev(esc->sc_dev, "switching to host mode\n");
+               usbmode &= ~TEGRA_EHCI_USBMODE_CM;
+               usbmode |= __SHIFTIN(TEGRA_EHCI_USBMODE_CM_HOST,
+                                    TEGRA_EHCI_USBMODE_CM);
+               bus_space_write_4(sc->sc_bst, sc->sc_bsh,
+                   TEGRA_EHCI_USBMODE_REG, usbmode);
+       }
+
+       /* Parallel transceiver select */
+       tegra_reg_set_clear(sc->sc_bst, sc->sc_bsh,
+           TEGRA_EHCI_HOSTPC1_DEVLC_REG,
+           __SHIFTIN(TEGRA_EHCI_HOSTPC1_DEVLC_PTS_UTMI,
+                     TEGRA_EHCI_HOSTPC1_DEVLC_PTS),
+           TEGRA_EHCI_HOSTPC1_DEVLC_PTS |
+           TEGRA_EHCI_HOSTPC1_DEVLC_STS);
+
+       bus_space_write_4(sc->sc_bst, sc->sc_bsh, TEGRA_EHCI_TXFILLTUNING_REG,
+           __SHIFTIN(0x10, TEGRA_EHCI_TXFILLTUNING_TXFIFOTHRES));
+}
+
+static void
+tegra_ehci_utmip_init(struct tegra_ehci_softc *sc)
+{
+       bus_space_tag_t bst = sc->sc_bst;
+       bus_space_handle_t bsh = sc->sc_bsh;
+       int retry;
+
+       /* Put UTMIP PHY into reset before programming UTMIP config registers */
+       tegra_reg_set_clear(bst, bsh, TEGRA_EHCI_SUSP_CTRL_REG,
+           TEGRA_EHCI_SUSP_CTRL_UTMIP_RESET, 0);
+
+       /* Enable UTMIP PHY mode */
+       tegra_reg_set_clear(bst, bsh, TEGRA_EHCI_SUSP_CTRL_REG,
+           TEGRA_EHCI_SUSP_CTRL_UTMIP_PHY_ENB, 0);
+
+       /* Stop crystal clock */
+       tegra_reg_set_clear(bst, bsh, TEGRA_EHCI_UTMIP_MISC_CFG1_REG,
+           0, TEGRA_EHCI_UTMIP_MISC_CFG1_PHY_XTAL_CLOCKEN);
+       delay(1);
+
+       /* Clear session status */
+       tegra_reg_set_clear(bst, bsh, TEGRA_EHCI_PHY_VBUS_SENSORS_REG,
+           0,
+           TEGRA_EHCI_PHY_VBUS_SENSORS_B_VLD_SW_VALUE |



Home | Main Index | Thread Index | Old Index