summaryrefslogtreecommitdiff
path: root/drivers/usb/cdns3/core.c
blob: 1962b077419b29696f3a2b8f00541513fdd7f6bd (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
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
// SPDX-License-Identifier: GPL-2.0+
/*
 * Copyright (C) 2016 Cadence Design Systems - https://www.cadence.com/
 * Copyright 2019 NXP
 */
#include <common.h>
#include <malloc.h>
#include <wait_bit.h>
#include <asm/dma-mapping.h>
#include <asm/io.h>
#include <linux/bug.h>
#include <linux/list.h>
#include <linux/compat.h>
#include <linux/usb/gadget.h>
#include <linux/usb/otg.h>

#include "cdns3-nxp-reg-def.h"
#include "core.h"
#include "gadget-export.h"
#include "gadget.h"

static void cdns3_reset_core(struct cdns3 *cdns)
{
	/* Set all Reset bits */
	setbits_le32(cdns->none_core_regs + USB3_CORE_CTRL1, ALL_SW_RESET);
	udelay(1);
}

static int cdns3_host_role_set(struct cdns3 *cdns)
{
	int ret;

	struct cdns3_generic_peripheral *priv = container_of(cdns,
			struct cdns3_generic_peripheral, cdns3);

	clrsetbits_le32(cdns->none_core_regs + USB3_CORE_CTRL1,
			MODE_STRAP_MASK, HOST_MODE | OC_DISABLE);
	clrbits_le32(cdns->none_core_regs + USB3_CORE_CTRL1,
		     PHYAHB_SW_RESET);
	mdelay(1);
	generic_phy_init(&priv->phy);
	setbits_le32(cdns->phy_regs + TB_ADDR_TX_RCVDETSC_CTRL,
		     RXDET_IN_P3_32KHZ);
	udelay(10);
	/* Force B Session Valid as 1 */
	writel(SET_FORCE_B_SESS_VALID, cdns->phy_regs + USB2_PHY_AFE_BC_REG4);
	mdelay(1);

	setbits_le32(cdns->none_core_regs + USB3_INT_REG, HOST_INT1_EN);

	clrbits_le32(cdns->none_core_regs + USB3_CORE_CTRL1,
		     ALL_SW_RESET);

	dev_dbg(cdns->dev, "wait xhci_power_on_ready\n");
	ret = wait_for_bit_le32(cdns->none_core_regs + USB3_CORE_STATUS,
				HOST_POWER_ON_READY, true, 100, false);
	if (ret) {
		dev_err(cdns->dev, "wait xhci_power_on_ready timeout\n");
		return ret;
	}

	return 0;
}

static int cdns3_gadget_role_set(struct cdns3 *cdns)
{
	int ret;

	struct cdns3_generic_peripheral *priv = container_of(cdns,
			struct cdns3_generic_peripheral, cdns3);

	clrsetbits_le32(cdns->none_core_regs + USB3_CORE_CTRL1,
			MODE_STRAP_MASK, DEV_MODE);
	clrbits_le32(cdns->none_core_regs + USB3_CORE_CTRL1,
		     PHYAHB_SW_RESET);

	generic_phy_init(&priv->phy);
	setbits_le32(cdns->phy_regs + TB_ADDR_TX_RCVDETSC_CTRL,
		     RXDET_IN_P3_32KHZ);
	udelay(10);
	/* Force B Session Valid as 1 */
	writel(SET_FORCE_B_SESS_VALID, cdns->phy_regs + USB2_PHY_AFE_BC_REG4);
	setbits_le32(cdns->none_core_regs + USB3_INT_REG, DEV_INT_EN);

	clrbits_le32(cdns->none_core_regs + USB3_CORE_CTRL1,
		     ALL_SW_RESET);

	dev_dbg(cdns->dev, "wait gadget_power_on_ready\n");
	ret = wait_for_bit_le32(cdns->none_core_regs + USB3_CORE_STATUS,
				DEV_POWER_ON_READY, true, 100, false);
	if (ret) {
		dev_err(cdns->dev, "wait gadget_power_on_ready timeout\n");
		return ret;
	}

	return 0;
}

static int cdns3_set_role(struct cdns3 *cdns, enum cdns3_roles role)
{
	int ret;

	if (role == CDNS3_ROLE_END)
		return -EPERM;

	/* Wait clk value */
	writel(CLK_VLD, cdns->none_core_regs + USB3_SSPHY_STATUS);
	ret = wait_for_bit_le32(cdns->none_core_regs + USB3_SSPHY_STATUS,
				CLK_VLD, true, 100, false);
	if (ret) {
		dev_err(cdns->dev, "wait clkvld timeout\n");
		return ret;
	}

	cdns3_reset_core(cdns);

	if (role == CDNS3_ROLE_HOST) {
		cdns3_host_role_set(cdns);
		dev_dbg(cdns->dev, "switch to host role successfully\n");
	} else { /* gadget mode */
		cdns3_gadget_role_set(cdns);
		dev_dbg(cdns->dev, "switch to gadget role successfully\n");
	}

	return 0;
}

static enum cdns3_roles cdns3_get_role(struct cdns3 *cdns)
{
	return cdns->roles[CDNS3_ROLE_HOST]
		? CDNS3_ROLE_HOST
		: CDNS3_ROLE_GADGET;
}

/**
 * cdns3_core_init_role - initialize role of operation
 * @cdns: Pointer to cdns3 structure
 * @dr_mode: Role mode of device
 *
 * Returns 0 on success otherwise negative errno
 */
static int cdns3_core_init_role(struct cdns3 *cdns, enum usb_dr_mode dr_mode)
{
	cdns->role = CDNS3_ROLE_END;
	if (dr_mode == USB_DR_MODE_UNKNOWN)
		dr_mode = USB_DR_MODE_OTG;

	/* Currently, only support gadget mode */
	if (dr_mode == USB_DR_MODE_OTG || dr_mode == USB_DR_MODE_HOST) {
		dev_err(cdns->dev, "doesn't support host and OTG, only for gadget\n");
		return -EPERM;
	}

	if (dr_mode == USB_DR_MODE_PERIPHERAL) {
		if (cdns3_gadget_init(cdns))
			dev_info(cdns->dev, "doesn't support gadget\n");
	}

	if (!cdns->roles[CDNS3_ROLE_HOST] && !cdns->roles[CDNS3_ROLE_GADGET]) {
		dev_err(cdns->dev, "no supported roles\n");
		return -ENODEV;
	}

	return 0;
}

static void cdns3_remove_roles(struct cdns3 *cdns)
{
	/* Only support gadget */
	cdns3_gadget_remove(cdns);
}

int cdns3_init(struct cdns3 *cdns)
{
	int ret;

	ret = cdns3_core_init_role(cdns, USB_DR_MODE_PERIPHERAL);

	cdns->role = cdns3_get_role(cdns);
	dev_dbg(dev, "the init role is %d\n", cdns->role);
	cdns3_set_role(cdns, cdns->role);
	ret = cdns3_role_start(cdns, cdns->role);
	if (ret) {
		dev_err(dev, "can't start %s role\n", cdns3_role(cdns)->name);
		goto err;
	}

	dev_dbg(dev, "Cadence USB3 core: probe succeed\n");

	return 0;

err:
	cdns3_remove_roles(cdns);

	return ret;
}

void cdns3_exit(struct cdns3 *cdns)
{
	cdns3_role_stop(cdns);
	cdns3_remove_roles(cdns);
	cdns3_reset_core(cdns);
}