msm_shared: dwc: DesignWare Cores USB 3.0 driver

Implements the dwc controller driver for usb 3.0 device
mode operation.

Change-Id: I36c42ba698371170ad1598dc592eae5e1d0c6856
diff --git a/platform/msm_shared/usb30_dwc_hw.c b/platform/msm_shared/usb30_dwc_hw.c
new file mode 100644
index 0000000..fd18f1a
--- /dev/null
+++ b/platform/msm_shared/usb30_dwc_hw.c
@@ -0,0 +1,639 @@
+/* Copyright (c) 2013, The Linux Foundation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * 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.
+ *     * Neither the name of The Linux Foundation nor the names of its
+ *       contributors may be used to endorse or promote products derived
+ *       from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER 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.
+ */
+#include <debug.h>
+#include <reg.h>
+#include <bits.h>
+#include <string.h>
+#include <malloc.h>
+#include <stdlib.h>
+#include <arch/defines.h>
+#include <platform/timer.h>
+#include <platform/interrupts.h>
+#include <platform/irqs.h>
+#include <kernel/event.h>
+#include <usb30_dwc_hwio.h>
+#include <usb30_dwc.h>
+#include <usb30_dwc_hw.h>
+
+extern char* ss_link_state_lookup[20];
+extern char* hs_link_state_lookup[20];
+extern char* event_lookup_device[20];
+extern char* event_lookup_ep[20];
+extern char* dev_ctrl_state_lookup[20];
+extern char* ep_state_lookup[20];
+extern char* dev_state_lookup[20];
+extern char* speed_lookup[20];
+
+//#define DEBUG_USB
+
+#ifdef DEBUG_USB
+#define DBG(...) dprintf(ALWAYS, __VA_ARGS__)
+#else
+#define DBG(...)
+#endif
+
+#define ERR(...) dprintf(ALWAYS, __VA_ARGS__)
+
+/* This file provides interface to interact with DWC hardware. This code
+ * does not maintain any soft states. It programs the h/w as requested by the
+ * APIs.
+ */
+
+/* generic api to send endpoint command */
+static void dwc_ep_cmd(dwc_dev_t *dev, uint8_t ep_phy_num, dwc_ep_cmd_t *ep_cmd)
+{
+	if(REG_READ_FIELDI(dev, GUSB2PHYCFG, 0, SUSPENDUSB20))
+	{
+		/* this must be 0. see snps 6.3.2.5.8 */
+		ASSERT(0);
+	}
+
+	/* wait until previous command is in-active */
+	while( REG_READ_FIELDI(dev, DEPCMD, ep_phy_num, CMDACT) == 1);
+
+	/* clear cmd reg */
+	REG_WRITEI(dev, DEPCMD, ep_phy_num, 0);
+
+	/* write the command parameters */
+	REG_WRITEI(dev, DEPCMDPAR2, ep_phy_num, ep_cmd->param2);
+	REG_WRITEI(dev, DEPCMDPAR1, ep_phy_num, ep_cmd->param1);
+	REG_WRITEI(dev, DEPCMDPAR0, ep_phy_num, ep_cmd->param0);
+
+	/* command */
+	REG_WRITE_FIELDI(dev, DEPCMD, ep_phy_num, CMDTYP, ep_cmd->cmd);
+
+	if ((ep_cmd->cmd == DEPCMD_CMD_UPDATE_TRANSFER) ||
+		(ep_cmd->cmd == DEPCMD_CMD_END_TRANSFER) ||
+		(ep_cmd->cmd == DEPCMD_CMD_START_NEW_CONF))
+	{
+		/* set the transfer resource index */
+		REG_WRITE_FIELDI(dev,
+						 DEPCMD,
+						 ep_phy_num,
+						 COMMANDPARAM,
+						 ep_cmd->xfer_resource_index);
+	}
+
+	/* command interrupt can be set only if device is in running state. */
+	if(dwc_device_run_status(dev))
+	{
+			REG_WRITE_FIELDI(dev, DEPCMD, ep_phy_num, CMDIOC, 1);
+	}
+
+	DBG("\nEP CMD: ep = %d : 0x%05x  "
+		"pram0 = 0x%08x param1 = 0x%08x param2 = 0x%08x",
+		ep_phy_num,
+		REG_READI(dev, DEPCMD, ep_phy_num) | (1 << 10),
+		ep_cmd->param0,
+		ep_cmd->param1,
+		ep_cmd->param2);
+
+	/* set active */
+	REG_WRITE_FIELDI(dev, DEPCMD, ep_phy_num, CMDACT, 1);
+
+	/* Wait until active bit is cleared.
+	 * This does not necessarily mean that command is executed.
+	 * It only means a new command can be issued.
+	 * We get an interrupt when command execution is complete.
+	 */
+	while( REG_READ_FIELDI(dev, DEPCMD, ep_phy_num, CMDACT) == 1);
+}
+
+/* send start transfer command to the specified ep.
+ * assumes the trb are already populated.
+ */
+void dwc_ep_cmd_start_transfer(dwc_dev_t *dev, uint8_t ep_phy_num)
+{
+	dwc_ep_cmd_t ep_cmd;
+	dwc_ep_t *ep = &dev->ep[DWC_EP_PHY_TO_INDEX(ep_phy_num)];
+
+	uint32_t td_addr_low  = (uint32_t) ep->trb;
+	uint32_t td_addr_high = (uint32_t) 0x0;
+
+	/* transfer descriptor (aka TRB list) address must be on 16 byte boundary.*/
+	ASSERT((td_addr_low  & 0xF) == 0);
+	ASSERT((td_addr_high & 0xF) == 0);
+
+	/* set command */
+	ep_cmd.cmd = DEPCMD_CMD_START_TRANSFER;
+
+	/* set params */
+	ep_cmd.param2 = 0;
+	ep_cmd.param1 = td_addr_low;
+	ep_cmd.param0 = td_addr_high;
+
+	dwc_ep_cmd(dev, ep_phy_num, &ep_cmd);
+
+	/* Note: On execution of this cmd, a ep command complete event occurs.
+	 * this DEPEVT ep event returns a XferRscIdx - transfer resource
+	 * index. That must be used to Update or End this transfer.
+	 */
+	DBG("\n START_TRANSFER: new EP phy_num = %d state is = %s",
+		ep_phy_num, ep_state_lookup[ep->state]);
+}
+
+/* end transfer on a particular endpoint */
+void dwc_ep_cmd_end_transfer(dwc_dev_t *dev, uint8_t ep_phy_num)
+{
+	dwc_ep_cmd_t ep_cmd;
+
+	dwc_ep_t *ep = &dev->ep[DWC_EP_PHY_TO_INDEX(ep_phy_num)];
+
+	ep_cmd.cmd = 0;
+
+	/* set cmd and the resource index */
+	ep_cmd.cmd                 = DEPCMD_CMD_END_TRANSFER;
+	ep_cmd.xfer_resource_index = ep->resource_idx;
+
+	/* params */
+	ep_cmd.param2 = 0;
+	ep_cmd.param1 = 0;
+	ep_cmd.param0 = 0;
+
+	/* note: TRB status is not updated by the h/w when end transfer is issued.
+	 * snps: 6.3.2.5.7
+	 */
+	dwc_ep_cmd(dev, ep_phy_num, &ep_cmd);
+}
+
+/* set number of transfer resources to be used for the ep. */
+void dwc_ep_cmd_set_transfer_resource(dwc_dev_t *dev, uint8_t ep_phy_num)
+{
+	dwc_ep_cmd_t ep_cmd;
+
+	/* set command */
+	ep_cmd.cmd = DEPCMD_CMD_SET_TR_CONF;
+
+	ep_cmd.param2 = 0;
+	ep_cmd.param1 = 0;
+	ep_cmd.param0 = 1; /* number of transfer resources: always set to 1 */
+
+	dwc_ep_cmd(dev, ep_phy_num, &ep_cmd);
+}
+
+/* Configure end point in the core before starting to use it. The following
+   parameters need to be configured:
+   - usb ep number
+   - ep direction
+   - ep type
+   - mak pkt size
+   - burst size
+   - transfer events to be generated for this ep
+   - for IN ep, tx fifo to be used
+*/
+void dwc_ep_cmd_set_config(dwc_dev_t *dev, uint8_t index, uint8_t action)
+{
+	uint8_t  ep_phy_num;
+	uint8_t  ep_usb_num;
+	uint8_t  ep_direction;
+	uint16_t max_pkt_size;
+	uint32_t burst_size;
+	uint8_t  tx_fifo_num;
+
+	dwc_ep_t      ep;
+	dwc_ep_cmd_t  ep_cmd;
+	dwc_ep_type_t ep_type;
+
+	ep = dev->ep[index];
+
+	/* get the corresponding physical ep number */
+	ep_phy_num    = ep.phy_num;
+	ep_usb_num    = ep.number;
+	ep_direction  = ep.dir;
+	ep_type       = ep.type;
+	max_pkt_size  = ep.max_pkt_size;
+	burst_size    = ep.burst_size;
+	tx_fifo_num   = ep.tx_fifo_num;
+
+	/* set command */
+	ep_cmd.cmd    = DEPCMD_CMD_SET_EP_CONF;
+	ep_cmd.param2 = 0x0;
+	ep_cmd.param1 = 0x0;
+	ep_cmd.param0 = 0x0;
+
+	/* TODO: set bInterval according to ep value.
+	 * ignored since it is not used for bulk.
+	 */
+
+	/* map this usb ep to the ep_phy_num */
+	ep_cmd.param1 |= ep_usb_num   << DEPCMDPAR1_USB_EP_NUM_BIT;
+	ep_cmd.param1 |= ep_direction << DEPCMDPAR1_USB_EP_DIR_BIT;
+
+	/* enable event generation */
+	ep_cmd.param1 |= BIT(DEPCMDPAR2_XFER_N_RDY_BIT);
+	ep_cmd.param1 |= BIT(DEPCMDPAR2_XFER_COMPLETE_BIT);
+
+	/* interrupt number: which event buffer to be used. */
+	ep_cmd.param1 |= 0;
+
+	/* action: 0 = initialize */
+	ep_cmd.param0 |= (action << DEPCMDPAR0_ACTION_BIT);
+	/* burst size */
+	ep_cmd.param0 |= (burst_size << DEPCMDPAR0_BURST_SIZE_BIT);
+
+	ep_cmd.param0 |= tx_fifo_num  << DEPCMDPAR0_FIFO_NUM_BIT;
+	ep_cmd.param0 |= ep_type      << DEPCMDPAR0_EP_TYPE_BIT;
+	ep_cmd.param0 |= max_pkt_size << DEPCMDPAR0_MAX_PKT_SIZE_BIT;
+
+	dwc_ep_cmd(dev, ep_phy_num, &ep_cmd);
+}
+
+/* send stall command to ep */
+void dwc_ep_cmd_stall(dwc_dev_t *dev, uint8_t ep_phy_num)
+{
+	dwc_ep_cmd_t ep_cmd;
+
+	/* set command */
+	ep_cmd.cmd = DEPCMD_CMD_SET_STALL;
+
+	ep_cmd.param2 = 0;
+	ep_cmd.param1 = 0;
+	ep_cmd.param0 = 0;
+
+	DBG("\nSTALLING.... ep_phy_num = %d\n", ep_phy_num);
+
+	dwc_ep_cmd(dev, ep_phy_num, &ep_cmd);
+}
+
+/* clear stall */
+void dwc_ep_cmd_clear_stall(dwc_dev_t *dev, uint8_t ep_phy_num)
+{
+	dwc_ep_cmd_t ep_cmd;
+
+	/* set command */
+	ep_cmd.cmd = DEPCMD_CMD_CLEAR_STALL;
+
+	ep_cmd.param2 = 0;
+	ep_cmd.param1 = 0;
+	ep_cmd.param0 = 0;
+
+	dwc_ep_cmd(dev, ep_phy_num, &ep_cmd);
+}
+
+/* send a start new config command */
+void dwc_ep_cmd_start_new_config(dwc_dev_t *dev,
+								 uint8_t ep_phy_num,
+								 uint8_t resource_idx)
+{
+	dwc_ep_cmd_t ep_cmd;
+
+	/* set command */
+	ep_cmd.cmd                 = DEPCMD_CMD_START_NEW_CONF;
+	ep_cmd.xfer_resource_index = resource_idx;
+
+	ep_cmd.param2 = 0;
+	ep_cmd.param1 = 0;
+	ep_cmd.param0 = 0;
+
+	dwc_ep_cmd(dev, ep_phy_num, &ep_cmd);
+}
+
+/******************** DWC Device related APIs *********************************/
+
+/* generic api to send device command */
+static void dwc_device_cmd(dwc_dev_t *dev, dwc_device_cmd_t *cmd)
+{
+	uint8_t active = REG_READ_FIELD(dev, DGCMD, CMDACT);
+
+	ASSERT(active);
+
+	REG_WRITE(dev, DGCMDPAR, cmd->param);
+	REG_WRITE_FIELD(dev, DGCMD, CMDTYP, cmd->cmd);
+
+	/* wait until active field is cleared. */
+	while(!REG_READ_FIELD(dev, DGCMD, CMDACT));
+
+	if(REG_READ_FIELD(dev, DGCMD, CMDSTATUS))
+	{
+		ERR("\n\n device command failed. \n\n");
+		ASSERT(0);
+	}
+}
+
+/* set periodic param */
+void dwc_device_set_periodic_param(dwc_dev_t *dev, uint32_t val)
+{
+	dwc_device_cmd_t cmd;
+
+	cmd.cmd   = DWC_DEV_CMD_SET_PERIODIC_PARAMS_VAL;
+	cmd.param = val;
+
+	/* send device command to set period param value */
+	dwc_device_cmd(dev, &cmd);
+}
+
+/* set device address */
+void dwc_device_set_addr(dwc_dev_t *dev, uint16_t addr)
+{
+	REG_WRITE_FIELD(dev, DCFG, DEVADDR, addr);
+}
+
+/* reset device */
+void dwc_device_reset(dwc_dev_t *dev)
+{
+	/* start reset */
+	REG_WRITE_FIELD(dev, DCTL, CSFTRST, 1);
+
+	/* wait until done */
+	while(REG_READ_FIELD(dev, DCTL, CSFTRST));
+}
+
+/* Run/Stop device: 1 == run. 0 == stop */
+void dwc_device_run(dwc_dev_t *dev, uint8_t run)
+{
+	REG_WRITE_FIELD(dev, DCTL, RUN_STOP, run);
+}
+
+/* is device running? */
+uint8_t dwc_device_run_status(dwc_dev_t *dev)
+{
+	return REG_READ_FIELD(dev, DCTL, RUN_STOP);
+}
+
+/******************** Managing various events *********************************/
+/* event init:
+   program event buffer address, size and reset event count to 0.
+ */
+void dwc_event_init(dwc_dev_t *dev)
+{
+	/* snps 8.2.2 */
+
+	/* event buffer address */
+	REG_WRITEI(dev, GEVNTADRLO, 0, (uint32_t) dev->event_buf.buf);
+	REG_WRITEI(dev, GEVNTADRHI, 0, 0x0);
+
+	/* set buffer size. assuming interrupt is always needed on new event,
+	 * bit 31 is not set.
+	 */
+	REG_WRITEI(dev, GEVNTSIZ, 0, dev->event_buf.buf_size);
+
+	/* reset count */
+	REG_WRITEI(dev, GEVNTCOUNT, 0, 0x0);
+}
+
+/* event update index */
+static void dwc_event_update_index(uint16_t *index, uint16_t max_count)
+{
+	if(*index == max_count)
+	{
+		/* we have read the last entry. Need to roll over for next reading.*/
+		*index = 0;
+	}
+	else
+	{
+		*index += 1;
+	}
+}
+
+/* Returns next event from event queue and the size of event
+ * Event buffer is a circular buffer that the hardware populates when any of
+ * the enabled event occurs. An interrupt is generated if interrupt is enabled
+ * for that event.
+ * This api returns the next valid event from the event buffer and updates event
+ * buffer index.
+ * Most events are 4 byte long
+ * Note: caller must provide at least 12 bytes buffer in case the
+ * next event is the special 12 byte event.
+ */
+uint16_t dwc_event_get_next(dwc_dev_t *dev, uint32_t *event)
+{
+	uint16_t count;
+	uint16_t event_size = 0;
+	uint32_t *buf;
+
+	/* read the number of valid event data in event buffer. */
+	count = REG_READI(dev, GEVNTCOUNT, 0);
+
+	if(count == 0)
+	{
+		/* no events in buffer. */
+		return event_size;
+	}
+
+	/* each event is at least 4 bytes long.
+	 * make sure there is at least one event to read.
+	 */
+	ASSERT(count >= 4);
+
+	/* get event buffer for this device */
+	buf = dev->event_buf.buf;
+
+	/* invalidate cached event buf data */
+	arch_invalidate_cache_range((addr_t) (buf + dev->event_buf.index), 4);
+
+	/* read next event */
+	*event = buf[dev->event_buf.index];
+	event_size += 4;
+	dwc_event_update_index(&dev->event_buf.index, dev->event_buf.max_index);
+
+
+	/* is this buffer overflow event? */
+	if((DWC_EVENT_DEVICE_EVENT_ID(*event) == DWC_EVENT_DEVICE_EVENT_ID_BUFFER_OVERFLOW))
+	{
+		/* ouch... */
+		ERR("\n Event buffer is full. Need to increase size.\n");
+		ASSERT(0);
+	}
+
+	/* check for that special 12 byte event */
+	if( DWC_EVENT_IS_DEVICE_EVENT(*event) &
+		(DWC_EVENT_DEVICE_EVENT_ID(*event) == DWC_EVENT_DEVICE_EVENT_ID_VENDOR_DEVICE_TEST_LMP))
+	{
+		/* invalidate cached event buf data */
+		arch_invalidate_cache_range((addr_t) (buf + dev->event_buf.index), 4);
+
+		*(event + 1) = buf[dev->event_buf.index];
+		event_size += 4;
+		dwc_event_update_index(&dev->event_buf.index, dev->event_buf.buf_size);
+
+		/* invalidate cached event buf data */
+		arch_invalidate_cache_range((addr_t) (buf + dev->event_buf.index), 4);
+
+		*(event + 1) = buf[dev->event_buf.index];
+		event_size += 4;
+		dwc_event_update_index(&dev->event_buf.index, dev->event_buf.buf_size);
+	}
+
+	return event_size;
+}
+
+/* Lets h/w know that we have processed "count" bytes of data from event buffer
+ * and it can use that space for new events.
+ * This must be done only after the event is processed.
+ */
+void dwc_event_processed(dwc_dev_t *dev, uint16_t count)
+{
+	REG_WRITEI(dev, GEVNTCOUNT, 0, count);
+}
+
+/* enable device event generation:
+ * events - bit map of events defined in dwc_event_device_event_id_t
+ */
+void dwc_event_device_enable(dwc_dev_t *dev, uint32_t events)
+{
+	REG_WRITE(dev, DEVTEN, events);
+}
+
+/*************** Generic APIs affecting overall controller ********************/
+
+/* reset HS and SS PHY's digital interface: UTMI + PIPE3 */
+void dwc_phy_digital_reset(dwc_dev_t *dev)
+{
+	REG_WRITE_FIELDI(dev, GUSB2PHYCFG,  0, PHYSOFTRST, 1);
+	REG_WRITE_FIELDI(dev, GUSB3PIPECTL, 0, PHYSOFTRST, 1);
+
+	/* per HPG */
+	udelay(100);
+
+	REG_WRITE_FIELDI(dev, GUSB2PHYCFG,  0, PHYSOFTRST, 0);
+	REG_WRITE_FIELDI(dev, GUSB3PIPECTL, 0, PHYSOFTRST, 0);
+
+	/* per HPG */
+	udelay(100);
+}
+
+void dwc_usb2_phy_soft_reset(dwc_dev_t *dev)
+{
+	REG_WRITE_FIELDI(dev, GUSB2PHYCFG,  0, PHYSOFTRST, 1);
+
+	udelay(100);
+
+	REG_WRITE_FIELDI(dev, GUSB2PHYCFG,  0, PHYSOFTRST, 0);
+
+	udelay(100);
+}
+
+/* workaround_12 as described in HPG */
+void dwc_ss_phy_workaround_12(dwc_dev_t *dev)
+{
+	/* 12. */
+	REG_WRITEI(dev, GUSB3PIPECTL, 0, 0x30C0003);
+}
+
+/*  AXI master config */
+void dwc_axi_master_config(dwc_dev_t *dev)
+{
+	uint32_t reg = 0;
+
+	reg = (DWC_GSBUSCFG0_INCR4BRSTENA_BMSK |
+		   DWC_GSBUSCFG0_INCR8BRSTENA_BMSK |
+		   DWC_GSBUSCFG0_INCR16BRSTENA_BMSK);
+
+	REG_WRITE(dev, GSBUSCFG0, reg);
+}
+
+/* read the controller id and version information */
+uint32_t dwc_coreid(dwc_dev_t *dev)
+{
+	return REG_READ(dev, GSNPSID);
+}
+
+/* read the current connection speed. */
+uint8_t dwc_connectspeed(dwc_dev_t *dev)
+{
+	return REG_READ_FIELD(dev, DSTS, CONNECTSPD);
+}
+
+/* disable all non-control EPs */
+void dwc_ep_disable_non_control(dwc_dev_t *dev)
+{
+	uint32_t reg = REG_READ(dev, DALEPENA);
+
+	/* clear all except the control IN and OUT ep */
+	reg &= 0x3;
+
+	REG_WRITE(dev, DALEPENA, reg);
+}
+
+/* disable a specific ep */
+void dwc_ep_disable(dwc_dev_t *dev, uint8_t ep_phy_num)
+{
+	uint32_t reg = REG_READ(dev, DALEPENA);
+
+	reg &= ~BIT(ep_phy_num);
+
+	REG_WRITE(dev, DALEPENA, reg);
+}
+
+/* enable a specific ep */
+void dwc_ep_enable(dwc_dev_t *dev, uint8_t ep_phy_num)
+{
+	uint32_t reg = REG_READ(dev, DALEPENA);
+
+	reg |= BIT(ep_phy_num);
+
+	REG_WRITE(dev, DALEPENA, reg);
+}
+
+/* global reset of controller.
+ * 1 == put in reset. 0 == out of reset
+ */
+void dwc_reset(dwc_dev_t *dev, uint8_t reset)
+{
+	/* snps databook table 6-11 indicates this field to be used only for debug
+	 * purpose. use dctl.softreset instead for devide mode.
+	 * but hpg 4.4.2. 8.a says use this.
+	 */
+	REG_WRITE_FIELD(dev, GCTL, CORESOFTRESET, reset);
+
+	/* per HPG */
+	udelay(100);
+}
+
+/* initialize global control reg for device mode operation.
+ * sequence numbers are as described in HPG.
+ */
+void dwc_gctl_init(dwc_dev_t *dev)
+{
+	/* 16. */
+	/* a. default value is good for RAM clock */
+	/* b. default value is good for Disable Debug Attach */
+	REG_WRITE_FIELD(dev, GCTL, DEBUGATTACH, 0);
+
+	/* c & d: disable loopback/local loopback
+	 * TODO: possibly for older version. no such fields in GCTL
+	 */
+
+	/* e. no soft reset. */
+	REG_WRITE_FIELD(dev, GCTL, CORESOFTRESET, 0);
+
+	/* f. set port capability direction: device */
+	REG_WRITE_FIELD(dev, GCTL, PRTCAPDIR, 0x2);
+
+	/* g. set scale down value */
+	REG_WRITE_FIELD(dev, GCTL, FRMSCLDWN, 0x0);
+
+	/* h. enable multiple attempts for SS connection */
+	REG_WRITE_FIELD(dev, GCTL, U2RSTECN, 1);
+
+	/* i. set power down scale of snps phy */
+	REG_WRITE_FIELD(dev, GCTL, PWRDNSCALE, 0x2);
+
+	/* j. clear SOFITPSYNC bit */
+	REG_WRITE_FIELD(dev, GCTL, SOFITPSYNC, 0);
+}