diff --git a/src/mainboard/google/nyan/mainboard.c b/src/mainboard/google/nyan/mainboard.c index 466c14efd3..a25c91cf5d 100644 --- a/src/mainboard/google/nyan/mainboard.c +++ b/src/mainboard/google/nyan/mainboard.c @@ -27,6 +27,7 @@ #include #include #include +#include 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); diff --git a/src/soc/nvidia/tegra/usb.c b/src/soc/nvidia/tegra/usb.c new file mode 100644 index 0000000000..0a3434fb0d --- /dev/null +++ b/src/soc/nvidia/tegra/usb.c @@ -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 +#include +#include +#include + +#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 */ +} diff --git a/src/soc/nvidia/tegra/usb.h b/src/soc/nvidia/tegra/usb.h new file mode 100644 index 0000000000..059a815317 --- /dev/null +++ b/src/soc/nvidia/tegra/usb.h @@ -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 + +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 diff --git a/src/soc/nvidia/tegra124/Makefile.inc b/src/soc/nvidia/tegra124/Makefile.inc index ceb32d72aa..7d45b6e14f 100644 --- a/src/soc/nvidia/tegra124/Makefile.inc +++ b/src/soc/nvidia/tegra124/Makefile.inc @@ -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 diff --git a/src/soc/nvidia/tegra124/clock.c b/src/soc/nvidia/tegra124/clock.c index 5b8910b5f6..02f10a5504 100644 --- a/src/soc/nvidia/tegra124/clock.c +++ b/src/soc/nvidia/tegra124/clock.c @@ -18,6 +18,7 @@ #include #include #include +#include #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); diff --git a/src/soc/nvidia/tegra124/include/soc/addressmap.h b/src/soc/nvidia/tegra124/include/soc/addressmap.h index c4bdaa8b8e..29598a9765 100644 --- a/src/soc/nvidia/tegra124/include/soc/addressmap.h +++ b/src/soc/nvidia/tegra124/include/soc/addressmap.h @@ -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 { diff --git a/src/soc/nvidia/tegra124/usb.h b/src/soc/nvidia/tegra124/usb.h new file mode 100644 index 0000000000..ec5a14464e --- /dev/null +++ b/src/soc/nvidia/tegra124/usb.h @@ -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 . + */ + +#ifndef __SOC_NVIDIA_TEGRA124_USB_H_ +#define __SOC_NVIDIA_TEGRA124_USB_H_ + +#include +#include +#include + +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_ */