summaryrefslogtreecommitdiffstats
path: root/arch/arm/mach-zynqmp/zynqmp.c
blob: 312325956a99f0a7c51f820722f625a466eb1801 (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
// SPDX-License-Identifier: GPL-2.0-only
/*
 * Copyright (C) 2020 Michael Tretter <m.tretter@pengutronix.de>
 */

#include <common.h>
#include <init.h>
#include <linux/types.h>
#include <bootsource.h>
#include <reset_source.h>

#define ZYNQMP_CRL_APB_BASE		0xff5e0000
#define ZYNQMP_CRL_APB_BOOT_MODE_USER	(ZYNQMP_CRL_APB_BASE + 0x200)
#define ZYNQMP_CRL_APB_RESET_REASON	(ZYNQMP_CRL_APB_BASE + 0x220)

/* PSJTAG interface, PS dedicated pins. */
#define ZYNQMP_CRL_APB_BOOT_MODE_PSJTAG        0x0
/* SPI 24-bit addressing */
#define ZYNQMP_CRL_APB_BOOT_MODE_QSPI24        0x1
/* SPI 32-bit addressing */
#define ZYNQMP_CRL_APB_BOOT_MODE_QSPI32        0x2
/* SD 2.0 card @ controller 0 */
#define ZYNQMP_CRL_APB_BOOT_MODE_SD0           0x3
/* SPI NAND flash */
#define ZYNQMP_CRL_APB_BOOT_MODE_NAND          0x4
/* SD 2.0 card @ controller 1 */
#define ZYNQMP_CRL_APB_BOOT_MODE_SD1           0x5
/* eMMC @ controller 1 */
#define ZYNQMP_CRL_APB_BOOT_MODE_EMMC          0x6
/* USB 2.0 */
#define ZYNQMP_CRL_APB_BOOT_MODE_USB           0x7
/* PJTAG connection 0 option. */
#define ZYNQMP_CRL_APB_BOOT_MODE_PJTAG0        0x8
/* PJTAG connection 1 option. */
#define ZYNQMP_CRL_APB_BOOT_MODE_PJTAG1        0x9
/* SD 3.0 card (level-shifted) @ controller 1 */
#define ZYNQMP_CRL_APB_BOOT_MODE_SD1LS         0xE

/* External POR: The PS_POR_B reset signal pin was asserted. */
#define ZYNQMP_CRL_APB_RESET_REASON_EXTERNAL   BIT(0)
/* Internal POR: A system error triggered a POR reset. */
#define ZYNQMP_CRL_APB_RESET_REASON_INTERNAL   BIT(1)
/* Internal system reset; A system error triggered a system reset. */
#define ZYNQMP_CRL_APB_RESET_REASON_PMU        BIT(2)
/* PS-only reset: Write to PMU_GLOBAL.GLOBAL_RESET [PS_ONLY_RST]. */
#define ZYNQMP_CRL_APB_RESET_REASON_PSONLY     BIT(3)
/* External system reset: The PS_SRST_B reset signal pin was asserted. */
#define ZYNQMP_CRL_APB_RESET_REASON_SRST       BIT(4)
/* Software system reset: Write to RESET_CTRL [soft_reset]. */
#define ZYNQMP_CRL_APB_RESET_REASON_SOFT       BIT(5)
/* Software debugger reset: Write to BLOCKONLY_RST [debug_only]. */
#define ZYNQMP_CRL_APB_RESET_REASON_DEBUG_SYS  BIT(6)

static void zynqmp_get_bootsource(enum bootsource *src, int *instance)
{
	u32 v;

	if (!src || !instance)
		return;

	v = readl(ZYNQMP_CRL_APB_BOOT_MODE_USER);
	v &= 0x0F;

	/* cf. Table 11-1 "Boot Modes" in UG1085 Zynq UltraScale+ Device TRM */
	switch (v) {
	case ZYNQMP_CRL_APB_BOOT_MODE_PSJTAG:
	case ZYNQMP_CRL_APB_BOOT_MODE_PJTAG0:
	case ZYNQMP_CRL_APB_BOOT_MODE_PJTAG1:
		*src = BOOTSOURCE_JTAG;
		*instance = 0;
		break;

	case ZYNQMP_CRL_APB_BOOT_MODE_QSPI24:
	case ZYNQMP_CRL_APB_BOOT_MODE_QSPI32:
		*src = BOOTSOURCE_SPI;
		*instance = 0;
		break;

	case ZYNQMP_CRL_APB_BOOT_MODE_SD0:
		*src = BOOTSOURCE_MMC;
		*instance = 0;
		break;

	case ZYNQMP_CRL_APB_BOOT_MODE_NAND:
		*src = BOOTSOURCE_SPI_NAND;
		*instance = 0;
		break;

	case ZYNQMP_CRL_APB_BOOT_MODE_SD1:
	case ZYNQMP_CRL_APB_BOOT_MODE_EMMC:
	case ZYNQMP_CRL_APB_BOOT_MODE_SD1LS:
		*src = BOOTSOURCE_MMC;
		*instance = 1;
		break;

	case ZYNQMP_CRL_APB_BOOT_MODE_USB:
		*src = BOOTSOURCE_USB;
		*instance = 0;
		break;

	default:
		*src = BOOTSOURCE_UNKNOWN;
		*instance = BOOTSOURCE_INSTANCE_UNKNOWN;
		break;
	}
}

struct zynqmp_reset_reason {
	u32 mask;
	enum reset_src_type type;
};

static const struct zynqmp_reset_reason reset_reasons[] = {
	{ ZYNQMP_CRL_APB_RESET_REASON_DEBUG_SYS,	RESET_JTAG },
	{ ZYNQMP_CRL_APB_RESET_REASON_SOFT,		RESET_RST },
	{ ZYNQMP_CRL_APB_RESET_REASON_SRST,		RESET_POR },
	{ ZYNQMP_CRL_APB_RESET_REASON_PSONLY,		RESET_POR },
	{ ZYNQMP_CRL_APB_RESET_REASON_PMU,		RESET_POR },
	{ ZYNQMP_CRL_APB_RESET_REASON_INTERNAL,		RESET_POR },
	{ ZYNQMP_CRL_APB_RESET_REASON_EXTERNAL,		RESET_POR },
	{ /* sentinel */ }
};

static enum reset_src_type zynqmp_get_reset_src(void)
{
	enum reset_src_type type = RESET_UKWN;
	unsigned int i;
	u32 val;

	val = readl(ZYNQMP_CRL_APB_RESET_REASON);

	for (i = 0; i < ARRAY_SIZE(reset_reasons); i++) {
		if (val & reset_reasons[i].mask) {
			type = reset_reasons[i].type;
			break;
		}
	}

	pr_info("ZynqMP reset reason %s (ZYNQMP_CRL_APB_RESET_REASON: 0x%08x)\n",
		reset_source_to_string(type), val);

	return type;
}

static int zynqmp_init(void)
{
	enum bootsource boot_src;
	int boot_instance;

	zynqmp_get_bootsource(&boot_src, &boot_instance);
	bootsource_set_raw(boot_src, boot_instance);

	reset_source_set(zynqmp_get_reset_src());

	return 0;
}
postcore_initcall(zynqmp_init);