summaryrefslogtreecommitdiff
path: root/drivers/usb/dwc3/dwc3-uniphier.c
blob: ab85428a7003b9f1487b00e2881fb20cd7aa198c (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
// SPDX-License-Identifier: GPL-2.0+
/*
 * UniPhier Specific Glue Layer for DWC3
 *
 * Copyright (C) 2016-2017 Socionext Inc.
 *   Author: Masahiro Yamada <yamada.masahiro@socionext.com>
 *   Author: Kunihiko Hayashi <hayashi.kunihiko@socionext.com>
 */

#include <dm.h>
#include <dm/lists.h>
#include <linux/bitops.h>
#include <linux/usb/gadget.h>

#include "core.h"
#include "gadget.h"
#include "dwc3-generic.h"

#define UNIPHIER_PRO4_DWC3_RESET	0x40
#define   UNIPHIER_PRO4_DWC3_RESET_XIOMMU	BIT(5)
#define   UNIPHIER_PRO4_DWC3_RESET_XLINK	BIT(4)
#define   UNIPHIER_PRO4_DWC3_RESET_PHY_SS	BIT(2)

#define UNIPHIER_PRO5_DWC3_RESET	0x00
#define   UNIPHIER_PRO5_DWC3_RESET_PHY_S1	BIT(17)
#define   UNIPHIER_PRO5_DWC3_RESET_PHY_S0	BIT(16)
#define   UNIPHIER_PRO5_DWC3_RESET_XLINK	BIT(15)
#define   UNIPHIER_PRO5_DWC3_RESET_XIOMMU	BIT(14)

#define UNIPHIER_PXS2_DWC3_RESET	0x00
#define   UNIPHIER_PXS2_DWC3_RESET_XLINK	BIT(15)

static void uniphier_pro4_dwc3_init(struct udevice *dev, int index,
				    enum usb_dr_mode mode)
{
	struct dwc3_glue_data *glue = dev_get_plat(dev);
	void *regs = map_physmem(glue->regs, glue->size, MAP_NOCACHE);
	u32 tmp;

	tmp = readl(regs + UNIPHIER_PRO4_DWC3_RESET);
	tmp &= ~UNIPHIER_PRO4_DWC3_RESET_PHY_SS;
	tmp |= UNIPHIER_PRO4_DWC3_RESET_XIOMMU | UNIPHIER_PRO4_DWC3_RESET_XLINK;
	writel(tmp, regs + UNIPHIER_PRO4_DWC3_RESET);

	unmap_physmem(regs, MAP_NOCACHE);
}

static void uniphier_pro5_dwc3_init(struct udevice *dev, int index,
				    enum usb_dr_mode mode)
{
	struct dwc3_glue_data *glue = dev_get_plat(dev);
	void *regs = map_physmem(glue->regs, glue->size, MAP_NOCACHE);
	u32 tmp;

	tmp = readl(regs + UNIPHIER_PRO5_DWC3_RESET);
	tmp &= ~(UNIPHIER_PRO5_DWC3_RESET_PHY_S1 |
		 UNIPHIER_PRO5_DWC3_RESET_PHY_S0);
	tmp |= UNIPHIER_PRO5_DWC3_RESET_XLINK | UNIPHIER_PRO5_DWC3_RESET_XIOMMU;
	writel(tmp, regs + UNIPHIER_PRO5_DWC3_RESET);

	unmap_physmem(regs, MAP_NOCACHE);
}

static void uniphier_pxs2_dwc3_init(struct udevice *dev, int index,
				    enum usb_dr_mode mode)
{
	struct dwc3_glue_data *glue = dev_get_plat(dev);
	void *regs = map_physmem(glue->regs, glue->size, MAP_NOCACHE);
	u32 tmp;

	tmp = readl(regs + UNIPHIER_PXS2_DWC3_RESET);
	tmp |= UNIPHIER_PXS2_DWC3_RESET_XLINK;
	writel(tmp, regs + UNIPHIER_PXS2_DWC3_RESET);

	unmap_physmem(regs, MAP_NOCACHE);
}

static int dwc3_uniphier_glue_get_ctrl_dev(struct udevice *dev, ofnode *node)
{
	struct udevice *child;
	const char *name;
	ofnode subnode;

	/*
	 * "controller reset" belongs to glue logic, and it should be
	 * accessible in .glue_configure() before access to the controller
	 * begins.
	 */
	ofnode_for_each_subnode(subnode, dev_ofnode(dev)) {
		name = ofnode_get_name(subnode);
		if (!strncmp(name, "reset", 5))
			device_bind_driver_to_node(dev, "uniphier-reset",
						   name, subnode, &child);
	}

	/* Get controller node that is placed separately from the glue node */
	*node = ofnode_by_compatible(dev_ofnode(dev->parent),
				     "socionext,uniphier-dwc3");

	return 0;
}

static const struct dwc3_glue_ops uniphier_pro4_dwc3_ops = {
	.glue_get_ctrl_dev = dwc3_uniphier_glue_get_ctrl_dev,
	.glue_configure = uniphier_pro4_dwc3_init,
};

static const struct dwc3_glue_ops uniphier_pro5_dwc3_ops = {
	.glue_get_ctrl_dev = dwc3_uniphier_glue_get_ctrl_dev,
	.glue_configure = uniphier_pro5_dwc3_init,
};

static const struct dwc3_glue_ops uniphier_pxs2_dwc3_ops = {
	.glue_get_ctrl_dev = dwc3_uniphier_glue_get_ctrl_dev,
	.glue_configure = uniphier_pxs2_dwc3_init,
};

static const struct udevice_id uniphier_dwc3_match[] = {
	{
		.compatible = "socionext,uniphier-pro4-dwc3-glue",
		.data = (ulong)&uniphier_pro4_dwc3_ops,
	},
	{
		.compatible = "socionext,uniphier-pro5-dwc3-glue",
		.data = (ulong)&uniphier_pro5_dwc3_ops,
	},
	{
		.compatible = "socionext,uniphier-pxs2-dwc3-glue",
		.data = (ulong)&uniphier_pxs2_dwc3_ops,
	},
	{
		.compatible = "socionext,uniphier-ld20-dwc3-glue",
		.data = (ulong)&uniphier_pxs2_dwc3_ops,
	},
	{
		.compatible = "socionext,uniphier-pxs3-dwc3-glue",
		.data = (ulong)&uniphier_pxs2_dwc3_ops,
	},
	{
		.compatible = "socionext,uniphier-nx1-dwc3-glue",
		.data = (ulong)&uniphier_pxs2_dwc3_ops,
	},
	{ /* sentinel */ }
};

U_BOOT_DRIVER(dwc3_uniphier_wrapper) = {
	.name = "uniphier-dwc3",
	.id = UCLASS_SIMPLE_BUS,
	.of_match = uniphier_dwc3_match,
	.bind = dwc3_glue_bind,
	.probe = dwc3_glue_probe,
	.remove = dwc3_glue_remove,
	.plat_auto = sizeof(struct dwc3_glue_data),
};