Hung-Te Lin | 2fc3b62 | 2013-10-21 21:43:03 +0800 | [diff] [blame] | 1 | /* |
| 2 | * This file is part of the coreboot project. |
| 3 | * |
| 4 | * Copyright 2013 Google Inc. |
| 5 | * |
| 6 | * This program is free software; you can redistribute it and/or modify |
| 7 | * it under the terms of the GNU General Public License as published by |
| 8 | * the Free Software Foundation; version 2 of the License. |
| 9 | * |
| 10 | * This program is distributed in the hope that it will be useful, |
| 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 13 | * GNU General Public License for more details. |
Hung-Te Lin | 2fc3b62 | 2013-10-21 21:43:03 +0800 | [diff] [blame] | 14 | */ |
| 15 | |
| 16 | #include <delay.h> |
| 17 | #include <arch/io.h> |
| 18 | #include <console/console.h> |
| 19 | #include <soc/clock.h> |
| 20 | |
| 21 | #include "usb.h" |
| 22 | |
Furquan Shaikh | 2296774 | 2014-08-06 09:53:55 -0700 | [diff] [blame] | 23 | struct utmip_ctlr { |
| 24 | u32 pll0; |
| 25 | u32 pll1; |
| 26 | u32 xcvr0; |
| 27 | u32 bias0; |
| 28 | u32 hsrx0; |
| 29 | u32 hsrx1; |
| 30 | u32 fslsrx0; |
| 31 | u32 fslsrx1; |
| 32 | u32 tx; |
| 33 | u32 misc0; |
| 34 | u32 misc1; |
| 35 | u32 debounce; |
| 36 | u32 batchrgr; |
| 37 | u32 spare; |
| 38 | u32 xcvr1; |
| 39 | u32 bias1; |
| 40 | u32 bias_sts; |
| 41 | u32 chrgr_debounce; |
| 42 | u32 misc_sts; |
| 43 | u32 pmc_wakeup; |
| 44 | }; |
| 45 | check_member(utmip_ctlr, pmc_wakeup, 0x84c - 0x800); |
| 46 | |
| 47 | struct usb_ctlr { |
| 48 | u32 id; |
| 49 | u32 _rsv0; |
| 50 | u32 host; |
| 51 | u32 device; |
| 52 | u32 txbuf; /* 0x010 */ |
| 53 | u32 rxbuf; |
| 54 | u32 _rsv1[58]; |
| 55 | u16 ehci_caplen; /* 0x100 */ |
| 56 | u16 ehci_version; |
| 57 | u32 ehci_hcsp; |
| 58 | u32 ehci_hccp; |
| 59 | u32 _rsv2[5]; |
| 60 | u32 dci_version; /* 0x120 */ |
| 61 | u32 dcc_params; |
| 62 | u32 extsts; |
| 63 | u32 extintr; |
| 64 | u32 ehci_usbcmd; /* 0x130 */ |
| 65 | u32 ehci_usbsts; |
| 66 | u32 ehci_usbintr; |
| 67 | u32 ehci_frindex; |
| 68 | u32 _rsv3; /* 0x140 */ |
| 69 | u32 ehci_periodic_base; |
| 70 | u32 ehci_async_base; |
| 71 | u32 async_ttsts; |
| 72 | u32 burst_size; /* 0x150 */ |
| 73 | u32 tx_fill_tuning; |
| 74 | u32 _rsv4; |
| 75 | u32 icusb_ctrl; |
| 76 | u32 ulpi_viewport; /* 0x160 */ |
| 77 | u32 _rsv5[4]; |
| 78 | u32 ehci_portsc; |
| 79 | u32 _rsv6[15]; |
| 80 | u32 lpm_ctrl; |
| 81 | u32 _rsv7[15]; |
| 82 | u32 otgsc; |
| 83 | u32 usb_mode; |
| 84 | u32 _rsv8; |
| 85 | u32 ep_nak; /* 0x200 */ |
| 86 | u32 ep_nak_enable; |
| 87 | u32 ep_setup; |
| 88 | u32 ep_init; |
| 89 | u32 ep_deinit; |
| 90 | u32 ep_sts; |
| 91 | u32 ep_complete; |
| 92 | u32 ep_ctrl[16]; |
| 93 | u32 _rsv9[105]; |
| 94 | u32 suspend_ctrl; /* 0x400 */ |
| 95 | u32 vbus_sensors; |
| 96 | u32 vbus_wakeup_id; |
| 97 | u32 alt_vbus_sts; |
| 98 | u32 legacy_ctrl; |
| 99 | u32 _rsv10[3]; |
| 100 | u32 interpacket_delay; |
| 101 | u32 _rsv11[27]; |
| 102 | u32 resume_delay; |
| 103 | u32 _rsv12; |
| 104 | u32 spare; |
| 105 | u32 _rsv13[9]; |
| 106 | u32 new_ctrl; |
| 107 | u32 _rsv14[207]; |
| 108 | struct utmip_ctlr utmip; /* 0x800 */ |
| 109 | }; |
| 110 | check_member(usb_ctlr, utmip, 0x800); |
| 111 | |
| 112 | /* |
| 113 | * Tegra EHCI controllers need their usb_mode, lpm_ctrl and tx_fill_tuning |
| 114 | * registers initialized after every EHCI reset and before any other actions |
| 115 | * (such as Run/Stop bit) are taken. We reset the controller here, set those |
| 116 | * registers and rely on the fact that libpayload doesn't reset EHCI controllers |
| 117 | * on initialization for whatever weird reason. This is ugly, fragile, and I |
| 118 | * really don't like it, but making this work will require an ugly hack one way |
| 119 | * or another so we might as well take the path of least resistance for now. |
| 120 | */ |
| 121 | static void usb_ehci_reset_and_prepare(struct usb_ctlr *usb, enum usb_phy_type type) |
Hung-Te Lin | 2fc3b62 | 2013-10-21 21:43:03 +0800 | [diff] [blame] | 122 | { |
Furquan Shaikh | 2296774 | 2014-08-06 09:53:55 -0700 | [diff] [blame] | 123 | int timeout = 1000; |
| 124 | |
Julius Werner | 2f37bd6 | 2015-02-19 14:51:15 -0800 | [diff] [blame] | 125 | write32(&usb->ehci_usbcmd, 1 << 1); /* Host Controller Reset */ |
Furquan Shaikh | 2296774 | 2014-08-06 09:53:55 -0700 | [diff] [blame] | 126 | /* TODO: Resets are long, find way to parallelize... or just use XHCI */ |
| 127 | while (--timeout && (read32(&usb->ehci_usbcmd) & 1 << 1)) |
| 128 | /* wait for HC to reset */; |
| 129 | |
| 130 | if (!timeout) { |
| 131 | printk(BIOS_ERR, "ERROR: EHCI(%p) reset timeout", usb); |
| 132 | return; |
| 133 | } |
| 134 | |
| 135 | /* Controller mode: HOST */ |
Julius Werner | 2f37bd6 | 2015-02-19 14:51:15 -0800 | [diff] [blame] | 136 | write32(&usb->usb_mode, 3 << 0); |
Furquan Shaikh | 2296774 | 2014-08-06 09:53:55 -0700 | [diff] [blame] | 137 | /* Parallel transceiver selct */ |
Julius Werner | 2f37bd6 | 2015-02-19 14:51:15 -0800 | [diff] [blame] | 138 | write32(&usb->lpm_ctrl, type << 29); |
Furquan Shaikh | 2296774 | 2014-08-06 09:53:55 -0700 | [diff] [blame] | 139 | /* Tx FIFO Burst thresh */ |
Julius Werner | 2f37bd6 | 2015-02-19 14:51:15 -0800 | [diff] [blame] | 140 | write32(&usb->tx_fill_tuning, 0x10 << 16); |
Furquan Shaikh | 2296774 | 2014-08-06 09:53:55 -0700 | [diff] [blame] | 141 | } |
| 142 | |
| 143 | /* Assume USBx clocked, out of reset, UTMI+ PLL set up, SAMP_x out of pwrdn */ |
| 144 | void usb_setup_utmip(void *usb_base) |
| 145 | { |
| 146 | struct usb_ctlr *usb = (struct usb_ctlr *)usb_base; |
| 147 | |
Hung-Te Lin | 2fc3b62 | 2013-10-21 21:43:03 +0800 | [diff] [blame] | 148 | /* KHz formulas were guessed from U-Boot constants. Formats unclear. */ |
Julius Werner | e57c303 | 2014-04-11 18:23:12 -0700 | [diff] [blame] | 149 | int khz = clock_get_pll_input_khz(); |
Hung-Te Lin | 2fc3b62 | 2013-10-21 21:43:03 +0800 | [diff] [blame] | 150 | |
| 151 | /* Stop UTMI+ crystal clock while we mess with its settings */ |
| 152 | clrbits_le32(&usb->utmip.misc1, 1 << 30); /* PHY_XTAL_CLKEN */ |
| 153 | udelay(1); |
| 154 | |
| 155 | /* Take stuff out of pwrdn and add some magic numbers from U-Boot */ |
Julius Werner | 2f37bd6 | 2015-02-19 14:51:15 -0800 | [diff] [blame] | 156 | write32(&usb->utmip.xcvr0, |
Julius Werner | 9418476 | 2015-02-19 20:19:23 -0800 | [diff] [blame] | 157 | 0x8 << 25 | /* HS slew rate [10:4] */ |
| 158 | 0x3 << 22 | /* HS driver output 'SETUP' [6:4] */ |
| 159 | 0 << 21 | /* LS bias selection */ |
| 160 | 0 << 18 | /* PDZI pwrdn */ |
| 161 | 0 << 16 | /* PD2 pwrdn */ |
| 162 | 0 << 14 | /* PD pwrdn */ |
| 163 | 1 << 13 | /* (rst) HS receiver terminations */ |
| 164 | 0x1 << 10 | /* (rst) LS falling slew rate */ |
| 165 | 0x1 << 8 | /* (rst) LS rising slew rate */ |
| 166 | 0x4 << 0); /* HS driver output 'SETUP' [3:0] */ |
| 167 | write32(&usb->utmip.xcvr1, |
| 168 | 0x7 << 18 | /* Termination range adjustment */ |
| 169 | 0 << 4 | /* PDDR pwrdn */ |
| 170 | 0 << 2 | /* PDCHRP pwrdn */ |
| 171 | 0 << 0); /* PDDISC pwrdn */ |
| 172 | write32(&usb->utmip.tx, |
| 173 | 1 << 19 | /* FS send initial J before sync(?) */ |
| 174 | 1 << 16 | /* (rst) Allow stuff error on SoP */ |
| 175 | 1 << 9); /* (rst) Check disc only on EoP */ |
Julius Werner | 2f37bd6 | 2015-02-19 14:51:15 -0800 | [diff] [blame] | 176 | write32(&usb->utmip.hsrx0, |
Julius Werner | 9418476 | 2015-02-19 20:19:23 -0800 | [diff] [blame] | 177 | 0x2 << 30 | /* (rst) Keep pattern on active */ |
| 178 | 1 << 28 | /* (rst) Realign inertia on pkt */ |
Stephen Barber | e724879 | 2015-06-16 13:44:13 -0700 | [diff] [blame] | 179 | 0x0 << 24 | /* (rst) edges-1 to move sampling */ |
Julius Werner | 9418476 | 2015-02-19 20:19:23 -0800 | [diff] [blame] | 180 | 0x3 << 21 | /* (rst) squelch delay on EoP */ |
| 181 | 0x11 << 15 | /* cycles until IDLE */ |
| 182 | 0x10 << 10); /* elastic input depth */ |
Hung-Te Lin | 2fc3b62 | 2013-10-21 21:43:03 +0800 | [diff] [blame] | 183 | |
| 184 | /* U-Boot claims the USBD values for these are used across all UTMI+ |
| 185 | * PHYs. That sounds so horribly wrong that I'm not going to implement |
| 186 | * it, but keep it in mind if we're ever not using the USBD port. */ |
Julius Werner | 2f37bd6 | 2015-02-19 14:51:15 -0800 | [diff] [blame] | 187 | write32(&usb->utmip.bias0, |
Julius Werner | 9418476 | 2015-02-19 20:19:23 -0800 | [diff] [blame] | 188 | 0x1 << 24 | /* HS disconnect detect level [2] */ |
| 189 | 1 << 23 | /* (rst) IDPD value */ |
| 190 | 1 << 22 | /* (rst) IDPD select */ |
| 191 | 1 << 11 | /* (rst) OTG pwrdn */ |
| 192 | 0 << 10 | /* bias pwrdn */ |
| 193 | 0x1 << 2 | /* HS disconnect detect level [1:0] */ |
| 194 | 0x2 << 0); /* HS squelch detect level */ |
Hung-Te Lin | 2fc3b62 | 2013-10-21 21:43:03 +0800 | [diff] [blame] | 195 | |
Julius Werner | 9418476 | 2015-02-19 20:19:23 -0800 | [diff] [blame] | 196 | write32(&usb->utmip.bias1, |
| 197 | khz / 2200 << 3 | /* bias pwrdn cycles (20us?) */ |
| 198 | 1 << 2 | /* (rst) VBUS wakeup pwrdn */ |
| 199 | 0 << 0); /* PDTRK pwrdn */ |
Hung-Te Lin | 2fc3b62 | 2013-10-21 21:43:03 +0800 | [diff] [blame] | 200 | |
Julius Werner | 9418476 | 2015-02-19 20:19:23 -0800 | [diff] [blame] | 201 | write32(&usb->utmip.debounce, |
| 202 | 0xffff << 16 | /* (rst) */ |
| 203 | 25 * khz / 10 << 0); /* TODO: what's this, really? */ |
Hung-Te Lin | 2fc3b62 | 2013-10-21 21:43:03 +0800 | [diff] [blame] | 204 | |
| 205 | udelay(1); |
| 206 | setbits_le32(&usb->utmip.misc1, 1 << 30); /* PHY_XTAL_CLKEN */ |
| 207 | |
Julius Werner | 9418476 | 2015-02-19 20:19:23 -0800 | [diff] [blame] | 208 | write32(&usb->suspend_ctrl, |
| 209 | 1 << 12 | /* UTMI+ enable */ |
| 210 | 0 << 11); /* UTMI+ reset */ |
Furquan Shaikh | 2296774 | 2014-08-06 09:53:55 -0700 | [diff] [blame] | 211 | |
| 212 | usb_ehci_reset_and_prepare(usb, USB_PHY_UTMIP); |
| 213 | printk(BIOS_DEBUG, "USB controller @ %p set up with UTMI+ PHY\n",usb_base); |
Hung-Te Lin | 2fc3b62 | 2013-10-21 21:43:03 +0800 | [diff] [blame] | 214 | } |