1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * Copyright (c) 2020-2021 SiFive, Inc
4  *
5  * Authors:
6  *   Pragnesh Patel <pragnesh.patel@sifive.com>
7  */
8 
9 #include <init.h>
10 #include <spl.h>
11 #include <misc.h>
12 #include <log.h>
13 #include <linux/delay.h>
14 #include <linux/io.h>
15 #include <asm/gpio.h>
16 #include <asm/arch/gpio.h>
17 #include <asm/arch/spl.h>
18 
19 #define UBRDG_RESET	SIFIVE_GENERIC_GPIO_NR(0, 7)
20 #define ULPI_RESET	SIFIVE_GENERIC_GPIO_NR(0, 9)
21 #define UHUB_RESET	SIFIVE_GENERIC_GPIO_NR(0, 11)
22 #define GEM_PHY_RESET	SIFIVE_GENERIC_GPIO_NR(0, 12)
23 
24 #define MODE_SELECT_REG		0x1000
25 #define MODE_SELECT_SPI		0x6
26 #define MODE_SELECT_SD		0xb
27 #define MODE_SELECT_MASK	GENMASK(3, 0)
28 
spl_reset_device_by_gpio(const char * label,int pin,int low_width)29 static inline int spl_reset_device_by_gpio(const char *label, int pin, int low_width)
30 {
31 	int ret;
32 
33 	ret = gpio_request(pin, label);
34 	if (ret) {
35 		debug("%s gpio request failed: %d\n", label, ret);
36 		return ret;
37 	}
38 
39 	ret = gpio_direction_output(pin, 1);
40 	if (ret) {
41 		debug("%s gpio direction set failed: %d\n", label, ret);
42 		return ret;
43 	}
44 
45 	udelay(1);
46 
47 	gpio_set_value(pin, 0);
48 	udelay(low_width);
49 	gpio_set_value(pin, 1);
50 
51 	return ret;
52 }
53 
spl_gemgxl_init(void)54 static inline int spl_gemgxl_init(void)
55 {
56 	int ret;
57 	/*
58 	 * GEMGXL init VSC8541 PHY reset sequence;
59 	 * leave pull-down active for 2ms
60 	 */
61 	udelay(2000);
62 	ret = spl_reset_device_by_gpio("gem_phy_reset", GEM_PHY_RESET, 1);
63 	mdelay(15);
64 
65 	return ret;
66 }
67 
spl_usb_pcie_bridge_init(void)68 static inline int spl_usb_pcie_bridge_init(void)
69 {
70 	return spl_reset_device_by_gpio("usb_pcie_bridge_reset", UBRDG_RESET, 3000);
71 }
72 
spl_usb_hub_init(void)73 static inline int spl_usb_hub_init(void)
74 {
75 	return spl_reset_device_by_gpio("usb_hub_reset", UHUB_RESET, 100);
76 }
77 
spl_ulpi_init(void)78 static inline int spl_ulpi_init(void)
79 {
80 	return spl_reset_device_by_gpio("ulpi_reset", ULPI_RESET, 1);
81 }
82 
spl_board_init_f(void)83 int spl_board_init_f(void)
84 {
85 	int ret;
86 
87 	ret = spl_soc_init();
88 	if (ret) {
89 		debug("HiFive Unmatched FU740 SPL init failed: %d\n", ret);
90 		goto end;
91 	}
92 
93 	ret = spl_gemgxl_init();
94 	if (ret) {
95 		debug("Gigabit ethernet PHY (VSC8541) init failed: %d\n", ret);
96 		goto end;
97 	}
98 
99 	ret = spl_usb_pcie_bridge_init();
100 	if (ret) {
101 		debug("USB Bridge (ASM1042A) init failed: %d\n", ret);
102 		goto end;
103 	}
104 
105 	ret = spl_usb_hub_init();
106 	if (ret) {
107 		debug("USB Hub (ASM1074) init failed: %d\n", ret);
108 		goto end;
109 	}
110 
111 	ret = spl_ulpi_init();
112 	if (ret) {
113 		debug("USB 2.0 PHY (USB3320C) init failed: %d\n", ret);
114 		goto end;
115 	}
116 
117 end:
118 	return ret;
119 }
120 
spl_boot_device(void)121 u32 spl_boot_device(void)
122 {
123 	u32 mode_select = readl((void *)MODE_SELECT_REG);
124 	u32 boot_device = mode_select & MODE_SELECT_MASK;
125 
126 	switch (boot_device) {
127 	case MODE_SELECT_SPI:
128 		return BOOT_DEVICE_SPI;
129 	case MODE_SELECT_SD:
130 		return BOOT_DEVICE_MMC1;
131 	default:
132 		debug("Unsupported boot device 0x%x but trying MMC1\n",
133 		      boot_device);
134 		return BOOT_DEVICE_MMC1;
135 	}
136 }
137 
138 #ifdef CONFIG_SPL_LOAD_FIT
board_fit_config_name_match(const char * name)139 int board_fit_config_name_match(const char *name)
140 {
141 	/* boot using first FIT config */
142 	return 0;
143 }
144 #endif
145