aboutsummaryrefslogtreecommitdiff
path: root/src/soc/broadcom/cygnus/usb.c
blob: d95efd1a87361e0cfe86a59ab11719f362fa03d3 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
/*
 * Copyright (C) 2014 Broadcom Corporation
 *
 * 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.
 *
 * This program is distributed "as is" WITHOUT ANY WARRANTY of any
 * kind, whether express or implied; without even the implied warranty
 * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 */

#include <arch/io.h>
#include <delay.h>
#include <console/console.h>
#include <soc/cygnus.h>

#define CDRU_USBPHY_CLK_RST_SEL_OFFSET			0x11b4
#define CDRU_USBPHY2_HOST_DEV_SEL_OFFSET		0x11b8
#define CDRU_SPARE_REG_0_OFFSET				0x1238
#define CRMU_USB_PHY_AON_CTRL_OFFSET			0x00028
#define CDRU_USB_DEV_SUSPEND_RESUME_CTRL_OFFSET		0x1210
#define CDRU_USBPHY_P2_STATUS_OFFSET			0x1200

#define CDRU_USB_DEV_SUSPEND_RESUME_CTRL_DISABLE	0
#define PHY2_DEV_HOST_CTRL_SEL_DEVICE			0
#define PHY2_DEV_HOST_CTRL_SEL_HOST			1
#define CDRU_USBPHY_P2_STATUS__USBPHY_ILDO_ON_FLAG	1
#define CDRU_USBPHY_P2_STATUS__USBPHY_PLL_LOCK		0
#define CRMU_USBPHY_P0_AFE_CORERDY_VDDC			1
#define CRMU_USBPHY_P0_RESETB				2
#define CRMU_USBPHY_P1_AFE_CORERDY_VDDC			9
#define CRMU_USBPHY_P1_RESETB				10
#define CRMU_USBPHY_P2_AFE_CORERDY_VDDC			17
#define CRMU_USBPHY_P2_RESETB				18

#define USB2_IDM_IDM_IO_CONTROL_DIRECT_OFFSET		0x0408
#define USB2_IDM_IDM_IO_CONTROL_DIRECT__clk_enable	0
#define USB2_IDM_IDM_RESET_CONTROL_OFFSET		0x0800
#define USB2_IDM_IDM_RESET_CONTROL__RESET		0

#define PLL_LOCK_RETRY_COUNT	1000
#define MAX_REGULATOR_NAME_LEN  25
#define NUM_PHYS		3

struct bcm_phy_instance {
	struct phy *generic_phy;
	int port;
	int host_mode; /* 1 - Host , 0 - device */
	int power; /* 1 -powered_on 0 -powered off */
	struct regulator *vbus_supply;
};

struct bcm_phy_driver {
	void *usbphy_regs;
	void *usb2h_idm_regs;
	void *usb2d_idm_regs;
	int num_phys, idm_host_enabled;
	struct bcm_phy_instance instances[NUM_PHYS];
};

static struct bcm_phy_driver phy_driver;

static int bcm_phy_init(struct bcm_phy_instance *instance_ptr)
{
	/* Only PORT 2 is capabale of being device and host
	 * Default setting is device, check if it is set to host */
	if (instance_ptr->port == 2) {
		if (instance_ptr->host_mode == PHY2_DEV_HOST_CTRL_SEL_HOST)
			write32(phy_driver.usbphy_regs + CDRU_USBPHY2_HOST_DEV_SEL_OFFSET,
				PHY2_DEV_HOST_CTRL_SEL_HOST);
		else
			die("usb device mode unsupported\n");
	}

	return 0;
}

static int bcm_phy_poweron(struct bcm_phy_instance *instance_ptr)
{
	int clock_reset_flag = 1;
	u32 val;

	/* Bring the AFE block out of reset to start powering up the PHY */
	val = read32(phy_driver.usbphy_regs + CRMU_USB_PHY_AON_CTRL_OFFSET);
	if (instance_ptr->port == 0)
		val |= (1 << CRMU_USBPHY_P0_AFE_CORERDY_VDDC);
	else if (instance_ptr->port == 1)
		val |= (1 << CRMU_USBPHY_P1_AFE_CORERDY_VDDC);
	else if (instance_ptr->port == 2)
		val |= (1 << CRMU_USBPHY_P2_AFE_CORERDY_VDDC);
	write32(phy_driver.usbphy_regs + CRMU_USB_PHY_AON_CTRL_OFFSET, val);

	instance_ptr->power = 1;

	/* Check if the port 2 is configured for device */
	if (instance_ptr->port == 2 &&
		instance_ptr->host_mode == PHY2_DEV_HOST_CTRL_SEL_DEVICE)
		die("usb device mode unsupported\n");

	val = read32(phy_driver.usbphy_regs + CDRU_USBPHY_CLK_RST_SEL_OFFSET);

	/* Check if the phy that is configured
	 * to provide clock and reset is powered on*/
	if (val >= 0 && val < phy_driver.num_phys) {
		if (phy_driver.instances[val].power == 1)
			clock_reset_flag = 0;
	}

	/* if not set the current phy */
	if (clock_reset_flag) {
		val = instance_ptr->port;
		write32(phy_driver.usbphy_regs + CDRU_USBPHY_CLK_RST_SEL_OFFSET,
			val);
	}

	if (phy_driver.idm_host_enabled != 1) {
		/* Enable clock to USB and get the USB out of reset */
		setbits_le32(phy_driver.usb2h_idm_regs +
			     USB2_IDM_IDM_IO_CONTROL_DIRECT_OFFSET,
			     (1 << USB2_IDM_IDM_IO_CONTROL_DIRECT__clk_enable));
		clrbits_le32(phy_driver.usb2h_idm_regs +
			     USB2_IDM_IDM_RESET_CONTROL_OFFSET,
			     (1 << USB2_IDM_IDM_RESET_CONTROL__RESET));
		phy_driver.idm_host_enabled = 1;
	}

	return 0;
}

static int bcm_phy_probe(void)
{
	int i;

	phy_driver.num_phys = NUM_PHYS;
	phy_driver.usbphy_regs = (void *)0x0301c000;
	phy_driver.usb2h_idm_regs = (void *)0x18115000;
	phy_driver.usb2d_idm_regs = (void *)0x18111000;
	phy_driver.idm_host_enabled = 0;

	/* Shutdown all ports. They can be powered up as required */
	clrbits_le32(phy_driver.usbphy_regs + CRMU_USB_PHY_AON_CTRL_OFFSET,
		     (1 << CRMU_USBPHY_P0_AFE_CORERDY_VDDC) |
		     (1 << CRMU_USBPHY_P0_RESETB) |
		     (1 << CRMU_USBPHY_P1_AFE_CORERDY_VDDC) |
		     (1 << CRMU_USBPHY_P1_RESETB) |
		     (1 << CRMU_USBPHY_P2_AFE_CORERDY_VDDC) |
		     (1 << CRMU_USBPHY_P2_RESETB));

	for (i = 0; i < phy_driver.num_phys; i++) {
		phy_driver.instances[i].port = i;
		phy_driver.instances[i].host_mode = PHY2_DEV_HOST_CTRL_SEL_HOST;
	}

	return 0;
}

void usb_init(void)
{
	bcm_phy_probe();
	/* currently, we only need thus support port 0 */
	bcm_phy_init(&phy_driver.instances[0]);
	bcm_phy_poweron(&phy_driver.instances[0]);
	printk(BIOS_INFO, "usb phy[%d] is powered on\n", 0);
}