1// SPDX-License-Identifier: GPL-2.0+
2/*
3 * Copyright (C) 2015-2017 Socionext Inc.
4 *   Author: Masahiro Yamada <yamada.masahiro@socionext.com>
5 */
6
7#include <command.h>
8#include <spl.h>
9#include <stdio.h>
10#include <linux/bitops.h>
11#include <linux/bug.h>
12#include <linux/errno.h>
13#include <linux/io.h>
14#include <linux/log2.h>
15#include <linux/printk.h>
16
17#include "../init.h"
18#include "../sg-regs.h"
19#include "../soc-info.h"
20#include "boot-device.h"
21
22#define SBBASE0			0x58c00100
23#define SBBASE_BANK_ENABLE		BIT(0)
24
25static int uniphier_sbc_boot_is_swapped(void)
26{
27	return !(readl(SBBASE0) & SBBASE_BANK_ENABLE);
28}
29
30struct uniphier_boot_device_info {
31	unsigned int soc_id;
32	unsigned int boot_device_sel_shift;
33	const struct uniphier_boot_device *boot_device_table;
34	const unsigned int *boot_device_count;
35	int (*boot_device_is_sd)(u32 pinmon);
36	int (*boot_device_is_usb)(u32 pinmon);
37	unsigned int (*boot_device_fixup)(unsigned int mode);
38	int (*boot_is_swapped)(void);
39	bool have_internal_stm;
40};
41
42static const struct uniphier_boot_device_info uniphier_boot_device_info[] = {
43#if defined(CONFIG_ARCH_UNIPHIER_LD4)
44	{
45		.soc_id = UNIPHIER_LD4_ID,
46		.boot_device_sel_shift = 1,
47		.boot_device_table = uniphier_ld4_boot_device_table,
48		.boot_device_count = &uniphier_ld4_boot_device_count,
49		.boot_is_swapped = uniphier_sbc_boot_is_swapped,
50		.have_internal_stm = true,
51	},
52#endif
53#if defined(CONFIG_ARCH_UNIPHIER_PRO4)
54	{
55		.soc_id = UNIPHIER_PRO4_ID,
56		.boot_device_sel_shift = 1,
57		.boot_device_table = uniphier_ld4_boot_device_table,
58		.boot_device_count = &uniphier_ld4_boot_device_count,
59		.boot_is_swapped = uniphier_sbc_boot_is_swapped,
60		.have_internal_stm = false,
61	},
62#endif
63#if defined(CONFIG_ARCH_UNIPHIER_SLD8)
64	{
65		.soc_id = UNIPHIER_SLD8_ID,
66		.boot_device_sel_shift = 1,
67		.boot_device_table = uniphier_ld4_boot_device_table,
68		.boot_device_count = &uniphier_ld4_boot_device_count,
69		.boot_is_swapped = uniphier_sbc_boot_is_swapped,
70		.have_internal_stm = true,
71	},
72#endif
73#if defined(CONFIG_ARCH_UNIPHIER_PRO5)
74	{
75		.soc_id = UNIPHIER_PRO5_ID,
76		.boot_device_sel_shift = 1,
77		.boot_device_table = uniphier_pro5_boot_device_table,
78		.boot_device_count = &uniphier_pro5_boot_device_count,
79		.boot_is_swapped = uniphier_sbc_boot_is_swapped,
80		.have_internal_stm = false,
81	},
82#endif
83#if defined(CONFIG_ARCH_UNIPHIER_PXS2)
84	{
85		.soc_id = UNIPHIER_PXS2_ID,
86		.boot_device_sel_shift = 1,
87		.boot_device_table = uniphier_pxs2_boot_device_table,
88		.boot_device_count = &uniphier_pxs2_boot_device_count,
89		.boot_device_is_usb = uniphier_pxs2_boot_device_is_usb,
90		.boot_device_fixup = uniphier_pxs2_boot_device_fixup,
91		.boot_is_swapped = uniphier_sbc_boot_is_swapped,
92		.have_internal_stm = false,
93	},
94#endif
95#if defined(CONFIG_ARCH_UNIPHIER_LD6B)
96	{
97		.soc_id = UNIPHIER_LD6B_ID,
98		.boot_device_sel_shift = 1,
99		.boot_device_table = uniphier_pxs2_boot_device_table,
100		.boot_device_count = &uniphier_pxs2_boot_device_count,
101		.boot_device_is_usb = uniphier_pxs2_boot_device_is_usb,
102		.boot_device_fixup = uniphier_pxs2_boot_device_fixup,
103		.boot_is_swapped = uniphier_sbc_boot_is_swapped,
104		.have_internal_stm = true,	/* STM on A-chip */
105	},
106#endif
107#if defined(CONFIG_ARCH_UNIPHIER_LD11)
108	{
109		.soc_id = UNIPHIER_LD11_ID,
110		.boot_device_sel_shift = 1,
111		.boot_device_table = uniphier_ld11_boot_device_table,
112		.boot_device_count = &uniphier_ld11_boot_device_count,
113		.boot_device_is_usb = uniphier_ld11_boot_device_is_usb,
114		.boot_is_swapped = uniphier_sbc_boot_is_swapped,
115		.have_internal_stm = true,
116	},
117#endif
118#if defined(CONFIG_ARCH_UNIPHIER_LD20)
119	{
120		.soc_id = UNIPHIER_LD20_ID,
121		.boot_device_sel_shift = 1,
122		.boot_device_table = uniphier_ld11_boot_device_table,
123		.boot_device_count = &uniphier_ld11_boot_device_count,
124		.boot_device_is_usb = uniphier_ld20_boot_device_is_usb,
125		.boot_is_swapped = uniphier_sbc_boot_is_swapped,
126		.have_internal_stm = true,
127	},
128#endif
129#if defined(CONFIG_ARCH_UNIPHIER_PXS3)
130	{
131		.soc_id = UNIPHIER_PXS3_ID,
132		.boot_device_sel_shift = 1,
133		.boot_device_table = uniphier_pxs3_boot_device_table,
134		.boot_device_count = &uniphier_pxs3_boot_device_count,
135		.boot_device_is_usb = uniphier_pxs3_boot_device_is_usb,
136		.boot_is_swapped = uniphier_sbc_boot_is_swapped,
137		.have_internal_stm = false,
138	},
139#endif
140};
141UNIPHIER_DEFINE_SOCDATA_FUNC(uniphier_get_boot_device_info,
142			     uniphier_boot_device_info)
143
144static unsigned int __uniphier_boot_device_raw(
145				const struct uniphier_boot_device_info *info)
146{
147	u32 pinmon;
148	unsigned int boot_sel;
149
150	if (info->boot_is_swapped && info->boot_is_swapped())
151		return BOOT_DEVICE_NOR;
152
153	pinmon = readl(sg_base + SG_PINMON0);
154
155	if (info->boot_device_is_sd && info->boot_device_is_sd(pinmon))
156		return BOOT_DEVICE_MMC2;
157
158	if (info->boot_device_is_usb && info->boot_device_is_usb(pinmon))
159		return BOOT_DEVICE_USB;
160
161	boot_sel = pinmon >> info->boot_device_sel_shift;
162
163	BUG_ON(!is_power_of_2(*info->boot_device_count));
164	boot_sel &= *info->boot_device_count - 1;
165
166	return info->boot_device_table[boot_sel].boot_device;
167}
168
169unsigned int uniphier_boot_device_raw(void)
170{
171	const struct uniphier_boot_device_info *info;
172
173	info = uniphier_get_boot_device_info();
174	if (!info) {
175		pr_err("unsupported SoC\n");
176		return BOOT_DEVICE_NONE;
177	}
178
179	return __uniphier_boot_device_raw(info);
180}
181
182u32 spl_boot_device(void)
183{
184	const struct uniphier_boot_device_info *info;
185	u32 raw_mode;
186
187	info = uniphier_get_boot_device_info();
188	if (!info) {
189		pr_err("unsupported SoC\n");
190		return BOOT_DEVICE_NONE;
191	}
192
193	raw_mode = __uniphier_boot_device_raw(info);
194
195	return info->boot_device_fixup ?
196				info->boot_device_fixup(raw_mode) : raw_mode;
197}
198
199int uniphier_have_internal_stm(void)
200{
201	const struct uniphier_boot_device_info *info;
202
203	info = uniphier_get_boot_device_info();
204	if (!info) {
205		pr_err("unsupported SoC\n");
206		return -ENOTSUPP;
207	}
208
209	return info->have_internal_stm;
210}
211
212int uniphier_boot_from_backend(void)
213{
214	return !!(readl(sg_base + SG_PINMON0) & BIT(27));
215}
216
217#ifndef CONFIG_SPL_BUILD
218
219static int do_pinmon(struct cmd_tbl *cmdtp, int flag, int argc,
220		     char *const argv[])
221{
222	const struct uniphier_boot_device_info *info;
223	u32 pinmon;
224	unsigned int boot_device_count, boot_sel;
225	int i;
226
227	info = uniphier_get_boot_device_info();
228	if (!info) {
229		pr_err("unsupported SoC\n");
230		return CMD_RET_FAILURE;
231	}
232
233	if (uniphier_have_internal_stm())
234		printf("STB Micon: %s\n",
235		       uniphier_boot_from_backend() ? "OFF" : "ON");
236
237	if (info->boot_is_swapped)
238		printf("Boot Swap: %s\n",
239		       info->boot_is_swapped() ? "ON" : "OFF");
240
241	pinmon = readl(sg_base + SG_PINMON0);
242
243	if (info->boot_device_is_sd)
244		printf("SD Boot:  %s\n",
245		       info->boot_device_is_sd(pinmon) ? "ON" : "OFF");
246
247	if (info->boot_device_is_usb)
248		printf("USB Boot:  %s\n",
249		       info->boot_device_is_usb(pinmon) ? "ON" : "OFF");
250
251	boot_device_count = *info->boot_device_count;
252
253	boot_sel = pinmon >> info->boot_device_sel_shift;
254	boot_sel &= boot_device_count - 1;
255
256	printf("\nBoot Mode Sel:\n");
257	for (i = 0; i < boot_device_count; i++)
258		printf(" %c %02x %s\n", i == boot_sel ? '*' : ' ', i,
259		       info->boot_device_table[i].desc);
260
261	return CMD_RET_SUCCESS;
262}
263
264U_BOOT_CMD(
265	pinmon,	1,	1,	do_pinmon,
266	"pin monitor",
267	""
268);
269
270#endif /* !CONFIG_SPL_BUILD */
271