summaryrefslogtreecommitdiff
path: root/arch/mips/lantiq/xway/reset.c
blob: be5fd29de523b72eb51ee7697ab8bbf86dd13307 (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
/*
 *  This program is free software; you can redistribute it and/or modify it
 *  under the terms of the GNU General Public License version 2 as published
 *  by the Free Software Foundation.
 *
 *  Copyright (C) 2010 John Crispin <john@phrozen.org>
 *  Copyright (C) 2013-2015 Lantiq Beteiligungs-GmbH & Co.KG
 */

#include <linux/init.h>
#include <linux/io.h>
#include <linux/ioport.h>
#include <linux/pm.h>
#include <linux/export.h>
#include <linux/delay.h>
#include <linux/of_address.h>
#include <linux/of_platform.h>
#include <linux/reset-controller.h>

#include <asm/reboot.h>

#include <lantiq_soc.h>

#include "../prom.h"

/* reset request register */
#define RCU_RST_REQ		0x0010
/* reset status register */
#define RCU_RST_STAT		0x0014

/* xbar BE flag */
#define RCU_AHB_ENDIAN          0x004C
#define RCU_VR9_BE_AHB1S        0x00000008

/* reboot bit */
#define RCU_RD_GPHY0_XRX200	BIT(31)
#define RCU_RD_SRST		BIT(30)
#define RCU_RD_GPHY1_XRX200	BIT(29)

/* reset cause */
#define RCU_STAT_SHIFT		26
/* boot selection */
#define RCU_BOOT_SEL(x)		((x >> 18) & 0x7)
#define RCU_BOOT_SEL_XRX200(x)	(((x >> 17) & 0xf) | ((x >> 8) & 0x10))

/* dwc2 USB configuration registers */
#define RCU_USB1CFG		0x0018
#define RCU_USB2CFG		0x0034

/* USB DMA endianness bits */
#define RCU_USBCFG_HDSEL_BIT	BIT(11)
#define RCU_USBCFG_HOST_END_BIT	BIT(10)
#define RCU_USBCFG_SLV_END_BIT	BIT(9)

/* USB reset bits */
#define RCU_USBRESET		0x0010

#define USBRESET_BIT		BIT(4)

#define RCU_USBRESET2		0x0048

#define USB1RESET_BIT		BIT(4)
#define USB2RESET_BIT		BIT(5)

#define RCU_CFG1A		0x0038
#define RCU_CFG1B		0x003C

/* USB PMU devices */
#define PMU_AHBM		BIT(15)
#define PMU_USB0		BIT(6)
#define PMU_USB1		BIT(27)

/* USB PHY PMU devices */
#define PMU_USB0_P		BIT(0)
#define PMU_USB1_P		BIT(26)

/* remapped base addr of the reset control unit */
static void __iomem *ltq_rcu_membase;
static struct device_node *ltq_rcu_np;

static void ltq_rcu_w32(uint32_t val, uint32_t reg_off)
{
	ltq_w32(val, ltq_rcu_membase + reg_off);
}

static uint32_t ltq_rcu_r32(uint32_t reg_off)
{
	return ltq_r32(ltq_rcu_membase + reg_off);
}

static void ltq_machine_restart(char *command)
{
	u32 val = ltq_rcu_r32(RCU_RST_REQ);

	if (of_device_is_compatible(ltq_rcu_np, "lantiq,rcu-xrx200"))
		val |= RCU_RD_GPHY1_XRX200 | RCU_RD_GPHY0_XRX200;

	val |= RCU_RD_SRST;

	local_irq_disable();
	ltq_rcu_w32(val, RCU_RST_REQ);
	unreachable();
}

static void ltq_machine_halt(void)
{
	local_irq_disable();
	unreachable();
}

static void ltq_machine_power_off(void)
{
	local_irq_disable();
	unreachable();
}

static void ltq_usb_init(void)
{
	/* Power for USB cores 1 & 2 */
	ltq_pmu_enable(PMU_AHBM);
	ltq_pmu_enable(PMU_USB0);
	ltq_pmu_enable(PMU_USB1);

	ltq_rcu_w32(ltq_rcu_r32(RCU_CFG1A) | BIT(0), RCU_CFG1A);
	ltq_rcu_w32(ltq_rcu_r32(RCU_CFG1B) | BIT(0), RCU_CFG1B);

	/* Enable USB PHY power for cores 1 & 2 */
	ltq_pmu_enable(PMU_USB0_P);
	ltq_pmu_enable(PMU_USB1_P);

	/* Configure cores to host mode */
	ltq_rcu_w32(ltq_rcu_r32(RCU_USB1CFG) & ~RCU_USBCFG_HDSEL_BIT,
		RCU_USB1CFG);
	ltq_rcu_w32(ltq_rcu_r32(RCU_USB2CFG) & ~RCU_USBCFG_HDSEL_BIT,
		RCU_USB2CFG);

	/* Select DMA endianness (Host-endian: big-endian) */
	ltq_rcu_w32((ltq_rcu_r32(RCU_USB1CFG) & ~RCU_USBCFG_SLV_END_BIT)
		| RCU_USBCFG_HOST_END_BIT, RCU_USB1CFG);
	ltq_rcu_w32(ltq_rcu_r32((RCU_USB2CFG) & ~RCU_USBCFG_SLV_END_BIT)
		| RCU_USBCFG_HOST_END_BIT, RCU_USB2CFG);

	/* Hard reset USB state machines */
	ltq_rcu_w32(ltq_rcu_r32(RCU_USBRESET) | USBRESET_BIT, RCU_USBRESET);
	udelay(50 * 1000);
	ltq_rcu_w32(ltq_rcu_r32(RCU_USBRESET) & ~USBRESET_BIT, RCU_USBRESET);

	/* Soft reset USB state machines */
	ltq_rcu_w32(ltq_rcu_r32(RCU_USBRESET2)
		| USB1RESET_BIT | USB2RESET_BIT, RCU_USBRESET2);
	udelay(50 * 1000);
	ltq_rcu_w32(ltq_rcu_r32(RCU_USBRESET2)
		& ~(USB1RESET_BIT | USB2RESET_BIT), RCU_USBRESET2);
}

static int __init mips_reboot_setup(void)
{
	struct resource res;

	ltq_rcu_np = of_find_compatible_node(NULL, NULL, "lantiq,rcu-xway");
	if (!ltq_rcu_np)
		ltq_rcu_np = of_find_compatible_node(NULL, NULL,
							"lantiq,rcu-xrx200");

	/* check if all the reset register range is available */
	if (!ltq_rcu_np)
		panic("Failed to load reset resources from devicetree");

	if (of_address_to_resource(ltq_rcu_np, 0, &res))
		panic("Failed to get rcu memory range");

	if (!request_mem_region(res.start, resource_size(&res), res.name))
		pr_err("Failed to request rcu memory");

	ltq_rcu_membase = ioremap_nocache(res.start, resource_size(&res));
	if (!ltq_rcu_membase)
		panic("Failed to remap core memory");

	if (of_machine_is_compatible("lantiq,ar9") ||
	    of_machine_is_compatible("lantiq,vr9"))
		ltq_usb_init();

	_machine_restart = ltq_machine_restart;
	_machine_halt = ltq_machine_halt;
	pm_power_off = ltq_machine_power_off;

	return 0;
}

arch_initcall(mips_reboot_setup);