tegra124: Add USB PLL, PHY and EHCI setup code

This patch adds the required bits and pieces to get USB set up correctly
for EHCI controllers with UTMI+ PHYs.

BUG=None
TEST=None

Change-Id: I89406ffa25fa96778965f8f1572fad8a74f10844
Signed-off-by: Julius Werner <jwerner@chromium.org>
Reviewed-on: https://chromium-review.googlesource.com/174651
Reviewed-by: Ronald Minnich <rminnich@chromium.org>
This commit is contained in:
Julius Werner 2013-10-17 11:52:21 -07:00 committed by chrome-internal-fetch
commit ecd5c398ff
7 changed files with 343 additions and 5 deletions

View file

@ -27,6 +27,7 @@
#include <soc/nvidia/tegra124/gpio.h>
#include <soc/nvidia/tegra124/pmc.h>
#include <soc/nvidia/tegra124/spi.h>
#include <soc/nvidia/tegra124/usb.h>
static struct clk_rst_ctlr *clk_rst = (void *)TEGRA_CLK_RST_BASE;
@ -167,6 +168,11 @@ static void setup_pinmux(void)
PINMUX_SDMMC4_DAT6_FUNC_SDMMC4 | pin_up);
pinmux_set_config(PINMUX_SDMMC4_DAT7_INDEX,
PINMUX_SDMMC4_DAT7_FUNC_SDMMC4 | pin_up);
/* TODO: This is supposed to work with the USB special function pinmux,
* but it doesn't. Go with GPIOs for now and solve the problem later. */
gpio_output_open_drain(GPIO(N4), 1); /* USB VBUS EN0 */
gpio_output_open_drain(GPIO(N5), 1); /* USB VBUS EN1 */
}
static void setup_kernel_info(void)
@ -194,16 +200,21 @@ static void setup_ec_spi(void)
static void mainboard_init(device_t dev)
{
setup_pinmux();
set_clock_sources();
clock_enable_clear_reset(CLK_L_GPIO | CLK_L_I2C1 | CLK_L_SDMMC4,
clock_enable_clear_reset(CLK_L_GPIO | CLK_L_I2C1 |
CLK_L_SDMMC4 | CLK_L_USBD,
CLK_H_EMC | CLK_H_I2C2 | CLK_H_SBC1 |
CLK_H_PMC | CLK_H_MEM,
CLK_H_PMC | CLK_H_MEM | CLK_H_USB3,
CLK_U_I2C3 | CLK_U_CSITE | CLK_U_SDMMC3,
CLK_V_I2C4,
CLK_W_DVFS);
usb_setup_utmip1();
/* USB2 is the camera, we don't need it in firmware */
usb_setup_utmip3();
setup_pinmux();
i2c_init(0);
i2c_init(1);
i2c_init(2);

120
src/soc/nvidia/tegra/usb.c Normal file
View file

@ -0,0 +1,120 @@
/*
* This file is part of the coreboot project.
*
* Copyright 2013 Google Inc.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; version 2 of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <delay.h>
#include <arch/io.h>
#include <console/console.h>
#include <soc/clock.h>
#include "usb.h"
/* Assume USBx clocked, out of reset, UTMI+ PLL set up, SAMP_x out of pwrdn */
void usb_setup_utmip(struct usb_ctlr *usb)
{
/* KHz formulas were guessed from U-Boot constants. Formats unclear. */
int khz = clock_get_osc_khz();
/* Stop UTMI+ crystal clock while we mess with its settings */
clrbits_le32(&usb->utmip.misc1, 1 << 30); /* PHY_XTAL_CLKEN */
udelay(1);
/* Take stuff out of pwrdn and add some magic numbers from U-Boot */
write32(0x8 << 25 | /* HS slew rate [10:4] */
0x3 << 22 | /* HS driver output 'SETUP' [6:4] */
0 << 21 | /* LS bias selection */
0 << 18 | /* PDZI pwrdn */
0 << 16 | /* PD2 pwrdn */
0 << 14 | /* PD pwrdn */
1 << 13 | /* (rst) HS receiver terminations */
0x1 << 10 | /* (rst) LS falling slew rate */
0x1 << 8 | /* (rst) LS rising slew rate */
0x4 << 0 | /* HS driver output 'SETUP' [3:0] */
0, &usb->utmip.xcvr0);
write32(0x7 << 18 | /* Termination range adjustment */
0 << 4 | /* PDDR pwrdn */
0 << 2 | /* PDCHRP pwrdn */
0 << 0 | /* PDDISC pwrdn */
0, &usb->utmip.xcvr1);
write32(1 << 19 | /* FS send initial J before sync(?) */
1 << 16 | /* (rst) Allow stuff error on SoP */
1 << 9 | /* (rst) Check disc only on EoP */
0, &usb->utmip.tx);
write32(0x2 << 30 | /* (rst) Keep pattern on active */
1 << 28 | /* (rst) Realign inertia on pkt */
0x1 << 24 | /* (rst) edges-1 to move sampling */
0x3 << 21 | /* (rst) squelch delay on EoP */
0x11 << 15 | /* cycles until IDLE */
0x10 << 10 | /* elastic input depth */
0, &usb->utmip.hsrx0);
/* U-Boot claims the USBD values for these are used across all UTMI+
* PHYs. That sounds so horribly wrong that I'm not going to implement
* it, but keep it in mind if we're ever not using the USBD port. */
write32(0x1 << 24 | /* HS disconnect detect level [2] */
1 << 23 | /* (rst) IDPD value */
1 << 22 | /* (rst) IDPD select */
1 << 11 | /* (rst) OTG pwrdn */
0 << 10 | /* bias pwrdn */
0x1 << 2 | /* HS disconnect detect level [1:0] */
0x2 << 0 | /* HS squelch detect level */
0, &usb->utmip.bias0);
write32(khz / 2200 << 3 | /* bias pwrdn cycles (20us?) */
1 << 2 | /* (rst) VBUS wakeup pwrdn */
0 << 0 | /* PDTRK pwrdn */
0, &usb->utmip.bias1);
write32(0xffff << 16 | /* (rst) */
25 * khz / 10 << 0 | /* TODO: what's this, really? */
0, &usb->utmip.debounce);
udelay(1);
setbits_le32(&usb->utmip.misc1, 1 << 30); /* PHY_XTAL_CLKEN */
write32(1 << 12 | /* UTMI+ enable */
0 << 11 | /* UTMI+ reset */
0, &usb->suspend_ctrl);
}
/*
* Tegra EHCI controllers need their usb_mode and lpm_ctrl registers initialized
* after every EHCI reset and before any other actions (such as Run/Stop bit)
* are taken. We reset the controller here, set those registers and rely on the
* fact that libpayload doesn't reset EHCI controllers on initialization for
* whatever weird reason. This is ugly, fragile, and I really don't like it, but
* making this work will require an ugly hack one way or another so we might as
* well take the path of least resistance for now.
*/
void usb_ehci_reset_and_prepare(struct usb_ctlr *usb, enum usb_phy_type type)
{
int timeout = 1000;
write32(1 << 1, &usb->ehci_usbcmd); /* Host Controller Reset */
/* TODO: Resets are long, find way to parallelize... or just use XHCI */
while (--timeout && (read32(&usb->ehci_usbcmd) & 1 << 1))
/* wait for HC to reset */;
if (!timeout) {
printk(BIOS_ERR, "ERROR: EHCI(%p) reset timeout", usb);
return;
}
write32(3 << 0, &usb->usb_mode); /* Controller mode: HOST */
write32(type << 29, &usb->lpm_ctrl); /* Parallel transceiver selct */
}

121
src/soc/nvidia/tegra/usb.h Normal file
View file

@ -0,0 +1,121 @@
/*
* This file is part of the coreboot project.
*
* Copyright 2013 Google Inc.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; version 2 of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef __SOC_NVIDIA_TEGRA_USB_H__
#define __SOC_NVIDIA_TEGRA_USB_H__
#include <stdint.h>
struct utmip_ctlr {
u32 pll0;
u32 pll1;
u32 xcvr0;
u32 bias0;
u32 hsrx0;
u32 hsrx1;
u32 fslsrx0;
u32 fslsrx1;
u32 tx;
u32 misc0;
u32 misc1;
u32 debounce;
u32 batchrgr;
u32 spare;
u32 xcvr1;
u32 bias1;
u32 bias_sts;
u32 chrgr_debounce;
u32 misc_sts;
u32 pmc_wakeup;
};
struct usb_ctlr {
u32 id;
u32 _rsv0;
u32 host;
u32 device;
u32 txbuf; /* 0x010 */
u32 rxbuf;
u32 _rsv1[58];
u16 ehci_caplen; /* 0x100 */
u16 ehci_version;
u32 ehci_hcsp;
u32 ehci_hccp;
u32 _rsv2[5];
u32 dci_version; /* 0x120 */
u32 dcc_params;
u32 extsts;
u32 extintr;
u32 ehci_usbcmd; /* 0x130 */
u32 ehci_usbsts;
u32 ehci_usbintr;
u32 ehci_frindex;
u32 _rsv3; /* 0x140 */
u32 ehci_periodic_base;
u32 ehci_async_base;
u32 async_ttsts;
u32 burst_size; /* 0x150 */
u32 tx_fill_tuning;
u32 _rsv4;
u32 icusb_ctrl;
u32 ulpi_viewport; /* 0x160 */
u32 _rsv5[4];
u32 ehci_portsc;
u32 _rsv6[15];
u32 lpm_ctrl;
u32 _rsv7[15];
u32 otgsc;
u32 usb_mode;
u32 _rsv8;
u32 ep_nak; /* 0x200 */
u32 ep_nak_enable;
u32 ep_setup;
u32 ep_init;
u32 ep_deinit;
u32 ep_sts;
u32 ep_complete;
u32 ep_ctrl[16];
u32 _rsv9[105];
u32 suspend_ctrl; /* 0x400 */
u32 vbus_sensors;
u32 vbus_wakeup_id;
u32 alt_vbus_sts;
u32 legacy_ctrl;
u32 _rsv10[3];
u32 interpacket_delay;
u32 _rsv11[27];
u32 resume_delay;
u32 _rsv12;
u32 spare;
u32 _rsv13[9];
u32 new_ctrl;
u32 _rsv14[207];
struct utmip_ctlr utmip; /* 0x800 */
};
enum usb_phy_type { /* For use in lpm_ctrl[31:29] */
USB_PHY_UTMIP = 0,
USB_PHY_ULPI = 2,
USB_PHY_ICUSB_SER = 3,
USB_PHY_HSIC = 4,
};
void usb_setup_utmip(struct usb_ctlr *usb);
void usb_ehci_reset_and_prepare(struct usb_ctlr *usb, enum usb_phy_type type);
#endif

View file

@ -50,6 +50,7 @@ ramstage-y += ../tegra/dp.c
ramstage-y += ../tegra/gpio.c
ramstage-y += ../tegra/i2c.c
ramstage-y += ../tegra/pinmux.c
ramstage-y += ../tegra/usb.c
ramstage-y += timer.c
ramstage-$(CONFIG_CONSOLE_SERIAL_UART) += uart.c

View file

@ -18,6 +18,7 @@
#include <arch/io.h>
#include <soc/addressmap.h>
#include <soc/clock.h>
#include <stdlib.h>
#include "clk_rst.h"
#include "cpug.h"
#include "flow.h"
@ -205,6 +206,37 @@ static void init_pll(u32 *base, u32 *misc, const union pll_fields pll)
writel(dividers | PLL_BASE_ENABLE, base);
}
static void init_utmip_pll(void)
{
int khz = clock_get_osc_khz();
/* Shut off PLL crystal clock while we mess with it */
clrbits_le32(&clk_rst->utmip_pll_cfg2, 1 << 30); /* PHY_XTAL_CLKEN */
udelay(1);
write32(80 << 16 | /* (rst) phy_divn */
1 << 8 | /* (rst) phy_divm */
0, &clk_rst->utmip_pll_cfg0); /* 960MHz * 1 / 80 == 12 MHz */
write32(div_round_up(khz, 8000) << 27 | /* pllu_enbl_cnt / 8 (1us) */
0 << 16 | /* PLLU pwrdn */
0 << 14 | /* pll_enable pwrdn */
0 << 12 | /* pll_active pwrdn */
div_round_up(khz, 102) << 0 | /* phy_stbl_cnt / 256 (2.5ms) */
0, &clk_rst->utmip_pll_cfg1);
/* TODO: TRM can't decide if actv is 5us or 10us, keep an eye on it */
write32(0 << 24 | /* SAMP_D/XDEV pwrdn */
div_round_up(khz, 3200) << 18 | /* phy_actv_cnt / 16 (5us) */
div_round_up(khz, 256) << 6 | /* pllu_stbl_cnt / 256 (1ms) */
0 << 4 | /* SAMP_C/USB3 pwrdn */
0 << 2 | /* SAMP_B/XHOST pwrdn */
0 << 0 | /* SAMP_A/USBD pwrdn */
0, &clk_rst->utmip_pll_cfg2);
setbits_le32(&clk_rst->utmip_pll_cfg2, 1 << 30); /* PHY_XTAL_CLKEN */
}
/* Initialize the UART and put it on CLK_M so we can use it during clock_init().
* Will later move it to PLLP in clock_config(). The divisor must be very small
* to accomodate 12KHz OSCs, so we override the 16.0 UART divider with the 15.1
@ -320,6 +352,7 @@ void clock_init(void)
init_pll(&clk_rst->pllc_base, &clk_rst->pllc_misc, osc_table[osc].pllc);
init_pll(&clk_rst->plld_base, &clk_rst->plld_misc, osc_table[osc].plld);
init_pll(&clk_rst->pllu_base, &clk_rst->pllu_misc, osc_table[osc].pllu);
init_utmip_pll();
val = (1 << CLK_SYS_RATE_AHB_RATE_SHIFT);
writel(val, &clk_rst->clk_sys_rate);

View file

@ -70,7 +70,9 @@ enum {
TEGRA_FUSE_BASE = TEGRA_APB_MISC_BASE + 0xF800,
TEGRA_CSITE_BASE = 0x70040000,
TEGRA_SYSCTR0_BASE = 0x700F0000,
TEGRA_USB_ADDR_MASK = 0xFFFFC000,
TEGRA_USBD_BASE = 0x7D000000,
TEGRA_USB2_BASE = 0x7D004000,
TEGRA_USB3_BASE = 0x7D008000,
};
enum {

View file

@ -0,0 +1,50 @@
/*
* This file is part of the coreboot project.
*
* Copyright 2013, Google Inc.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef __SOC_NVIDIA_TEGRA124_USB_H_
#define __SOC_NVIDIA_TEGRA124_USB_H_
#include <console/console.h>
#include <soc/addressmap.h>
#include <soc/nvidia/tegra/usb.h>
static inline void usb_setup_utmip1(void)
{
struct usb_ctlr *usb = (void *)TEGRA_USBD_BASE;
usb_setup_utmip(usb);
usb_ehci_reset_and_prepare(usb, USB_PHY_UTMIP);
printk(BIOS_DEBUG, "USBD controller set up with UTMI+ PHY\n");
}
static inline void usb_setup_utmip2(void)
{
struct usb_ctlr *usb = (void *)TEGRA_USB2_BASE;
usb_setup_utmip(usb);
usb_ehci_reset_and_prepare(usb, USB_PHY_UTMIP);
printk(BIOS_DEBUG, "USB2 controller set up with UTMI+ PHY\n");
}
static inline void usb_setup_utmip3(void)
{
struct usb_ctlr *usb = (void *)TEGRA_USB3_BASE;
usb_setup_utmip(usb);
usb_ehci_reset_and_prepare(usb, USB_PHY_UTMIP);
printk(BIOS_DEBUG, "USB3 controller set up with UTMI+ PHY\n");
}
#endif /* __SOC_NVIDIA_TEGRA124_USB_H_ */