blob: 43456e8af9958dc8dc757608d34c66507ef804fc [file] [log] [blame]
Marcel Ziswiler475ceff2019-05-31 19:00:20 +03001// SPDX-License-Identifier: GPL-2.0+
2/*
3 * Copyright 2019 Toradex
4 */
5
6#include <common.h>
Simon Glassafb02152019-12-28 10:45:01 -07007#include <cpu_func.h>
Simon Glassa7b51302019-11-14 12:57:46 -07008#include <init.h>
Simon Glass3ba929a2020-10-30 21:38:53 -06009#include <asm/global_data.h>
Marcel Ziswiler475ceff2019-05-31 19:00:20 +030010
11#include <asm/arch/clock.h>
12#include <asm/arch/imx8-pins.h>
13#include <asm/arch/iomux.h>
14#include <asm/arch/sci/sci.h>
15#include <asm/arch/sys_proto.h>
16#include <asm/gpio.h>
17#include <asm/io.h>
Marcel Ziswiler79d300c2023-01-16 20:04:56 +010018#include <command.h>
Simon Glass5e6201b2019-08-01 09:46:51 -060019#include <env.h>
Marcel Ziswiler475ceff2019-05-31 19:00:20 +030020#include <errno.h>
Philippe Schenker0e2980d2022-05-09 18:58:16 +020021#include <linux/bitops.h>
Marcel Ziswiler79d300c2023-01-16 20:04:56 +010022#include <linux/delay.h>
23#include <linux/libfdt.h>
Marcel Ziswiler475ceff2019-05-31 19:00:20 +030024
25#include "../common/tdx-cfg-block.h"
26
27DECLARE_GLOBAL_DATA_PTR;
28
29#define UART_PAD_CTRL ((SC_PAD_CONFIG_OUT_IN << PADRING_CONFIG_SHIFT) | \
30 (SC_PAD_ISO_OFF << PADRING_LPCONFIG_SHIFT) | \
31 (SC_PAD_28FDSOI_DSE_DV_HIGH << PADRING_DSE_SHIFT) | \
32 (SC_PAD_28FDSOI_PS_PU << PADRING_PULL_SHIFT))
33
Marcel Ziswiler79d300c2023-01-16 20:04:56 +010034#define PCB_VERS_DETECT ((SC_PAD_CONFIG_NORMAL << PADRING_CONFIG_SHIFT) | \
35 (SC_PAD_ISO_OFF << PADRING_LPCONFIG_SHIFT) | \
36 (SC_PAD_28FDSOI_DSE_DV_HIGH << PADRING_DSE_SHIFT) | \
37 (SC_PAD_28FDSOI_PS_PU << PADRING_PULL_SHIFT))
38
39#define GPIO_PAD_CTRL ((SC_PAD_CONFIG_NORMAL << PADRING_CONFIG_SHIFT) | \
40 (SC_PAD_ISO_OFF << PADRING_LPCONFIG_SHIFT) | \
41 (SC_PAD_28FDSOI_DSE_DV_HIGH << PADRING_DSE_SHIFT) | \
42 (SC_PAD_28FDSOI_PS_PU << PADRING_PULL_SHIFT))
43
44#define PCB_VERS_DEFAULT ((SC_PAD_CONFIG_NORMAL << PADRING_CONFIG_SHIFT) | \
45 (SC_PAD_ISO_OFF << PADRING_LPCONFIG_SHIFT) | \
46 (SC_PAD_28FDSOI_PS_PD << PADRING_PULL_SHIFT) | \
47 (SC_PAD_28FDSOI_DSE_DV_HIGH << PADRING_DSE_SHIFT))
48
Philippe Schenker0e2980d2022-05-09 18:58:16 +020049#define TDX_USER_FUSE_BLOCK1_A 276
50#define TDX_USER_FUSE_BLOCK1_B 277
51#define TDX_USER_FUSE_BLOCK2_A 278
52#define TDX_USER_FUSE_BLOCK2_B 279
53
Marcel Ziswiler79d300c2023-01-16 20:04:56 +010054enum pcb_rev_t {
55 PCB_VERSION_1_0,
56 PCB_VERSION_1_1
57};
58
59static iomux_cfg_t pcb_vers_detect[] = {
60 SC_P_MIPI_DSI0_GPIO0_00 | MUX_MODE_ALT(3) | MUX_PAD_CTRL(PCB_VERS_DETECT),
61 SC_P_MIPI_DSI0_GPIO0_01 | MUX_MODE_ALT(3) | MUX_PAD_CTRL(PCB_VERS_DETECT),
62};
63
64static iomux_cfg_t pcb_vers_default[] = {
65 SC_P_MIPI_DSI0_GPIO0_00 | MUX_MODE_ALT(3) | MUX_PAD_CTRL(PCB_VERS_DEFAULT),
66 SC_P_MIPI_DSI0_GPIO0_01 | MUX_MODE_ALT(3) | MUX_PAD_CTRL(PCB_VERS_DEFAULT),
67};
68
Marcel Ziswiler475ceff2019-05-31 19:00:20 +030069static iomux_cfg_t uart1_pads[] = {
70 SC_P_UART1_RX | MUX_PAD_CTRL(UART_PAD_CTRL),
71 SC_P_UART1_TX | MUX_PAD_CTRL(UART_PAD_CTRL),
72};
73
Philippe Schenker0e2980d2022-05-09 18:58:16 +020074struct tdx_user_fuses {
75 u16 pid4;
76 u16 vers;
77 u8 ramid;
78};
79
Marcel Ziswiler475ceff2019-05-31 19:00:20 +030080static void setup_iomux_uart(void)
81{
82 imx8_iomux_setup_multiple_pads(uart1_pads, ARRAY_SIZE(uart1_pads));
83}
84
Philippe Schenker0e2980d2022-05-09 18:58:16 +020085static uint32_t do_get_tdx_user_fuse(int a, int b)
86{
87 sc_err_t sciErr;
88 u32 val_a = 0;
89 u32 val_b = 0;
90
91 sciErr = sc_misc_otp_fuse_read(-1, a, &val_a);
92 if (sciErr != SC_ERR_NONE) {
93 printf("Error reading out user fuse %d\n", a);
94 return 0;
95 }
96
97 sciErr = sc_misc_otp_fuse_read(-1, b, &val_b);
98 if (sciErr != SC_ERR_NONE) {
99 printf("Error reading out user fuse %d\n", b);
100 return 0;
101 }
102
103 return ((val_a & 0xffff) << 16) | (val_b & 0xffff);
104}
105
106static void get_tdx_user_fuse(struct tdx_user_fuses *tdxuserfuse)
107{
108 u32 fuse_block;
109
110 fuse_block = do_get_tdx_user_fuse(TDX_USER_FUSE_BLOCK2_A,
111 TDX_USER_FUSE_BLOCK2_B);
112
113 /*
114 * Fuse block 2 acts as a backup area, if this reads 0 we want to
115 * use fuse block 1
116 */
117 if (fuse_block == 0)
118 fuse_block = do_get_tdx_user_fuse(TDX_USER_FUSE_BLOCK1_A,
119 TDX_USER_FUSE_BLOCK1_B);
120
121 tdxuserfuse->pid4 = (fuse_block >> 18) & GENMASK(13, 0);
122 tdxuserfuse->vers = (fuse_block >> 4) & GENMASK(13, 0);
123 tdxuserfuse->ramid = fuse_block & GENMASK(3, 0);
124}
125
Igor Opaniuk96b90ba2020-10-22 11:21:41 +0300126void board_mem_get_layout(u64 *phys_sdram_1_start,
127 u64 *phys_sdram_1_size,
128 u64 *phys_sdram_2_start,
129 u64 *phys_sdram_2_size)
130{
131 u32 is_quadplus = 0, val = 0;
Philippe Schenker0e2980d2022-05-09 18:58:16 +0200132 struct tdx_user_fuses tdxramfuses;
Igor Opaniuk96b90ba2020-10-22 11:21:41 +0300133 sc_err_t scierr = sc_misc_otp_fuse_read(-1, 6, &val);
134
135 if (scierr == SC_ERR_NONE) {
136 /* QP has one A72 core disabled */
137 is_quadplus = ((val >> 4) & 0x3) != 0x0;
138 }
139
Philippe Schenker0e2980d2022-05-09 18:58:16 +0200140 get_tdx_user_fuse(&tdxramfuses);
141
Igor Opaniuk96b90ba2020-10-22 11:21:41 +0300142 *phys_sdram_1_start = PHYS_SDRAM_1;
143 *phys_sdram_1_size = PHYS_SDRAM_1_SIZE;
144 *phys_sdram_2_start = PHYS_SDRAM_2;
Philippe Schenker0e2980d2022-05-09 18:58:16 +0200145
146 switch (tdxramfuses.ramid) {
147 case 1:
148 *phys_sdram_2_size = SZ_2G;
149 break;
150 case 2:
Igor Opaniuk96b90ba2020-10-22 11:21:41 +0300151 *phys_sdram_2_size = 0x0UL;
Philippe Schenker0e2980d2022-05-09 18:58:16 +0200152 break;
153 case 3:
154 *phys_sdram_2_size = SZ_2G;
155 break;
156 case 4:
157 *phys_sdram_2_size = SZ_4G + SZ_2G;
158 break;
159 default:
160 if (is_quadplus)
161 /* Our QP based SKUs only have 2 GB RAM (PHYS_SDRAM_1_SIZE) */
162 *phys_sdram_2_size = 0x0UL;
163 else
164 *phys_sdram_2_size = PHYS_SDRAM_2_SIZE;
165 break;
166 }
Igor Opaniuk96b90ba2020-10-22 11:21:41 +0300167}
168
Marcel Ziswiler475ceff2019-05-31 19:00:20 +0300169int board_early_init_f(void)
170{
Anatolij Gustschinef156d22019-06-12 13:35:25 +0200171 sc_pm_clock_rate_t rate = SC_80MHZ;
Philippe Schenker8ae977ce2023-01-16 20:05:10 +0100172 int ret;
Marcel Ziswiler475ceff2019-05-31 19:00:20 +0300173
Anatolij Gustschinef156d22019-06-12 13:35:25 +0200174 /* Set UART1 clock root to 80 MHz and enable it */
Philippe Schenker8ae977ce2023-01-16 20:05:10 +0100175 ret = sc_pm_setup_uart(SC_R_UART_1, rate);
176 if (ret)
177 return ret;
Marcel Ziswiler475ceff2019-05-31 19:00:20 +0300178
179 setup_iomux_uart();
180
181 return 0;
182}
183
Simon Glassfa4689a2019-12-06 21:41:35 -0700184#if CONFIG_IS_ENABLED(DM_GPIO)
Marcel Ziswilerd5ee5f82023-01-16 20:04:57 +0100185
186#define BKL1_GPIO IMX_GPIO_NR(1, 10)
187
188static iomux_cfg_t board_gpios[] = {
189 SC_P_LVDS1_GPIO00 | MUX_MODE_ALT(3) | MUX_PAD_CTRL(GPIO_PAD_CTRL),
190};
191
Marcel Ziswiler475ceff2019-05-31 19:00:20 +0300192static void board_gpio_init(void)
193{
Marcel Ziswilerd5ee5f82023-01-16 20:04:57 +0100194 imx8_iomux_setup_multiple_pads(board_gpios, ARRAY_SIZE(board_gpios));
195
196 gpio_request(BKL1_GPIO, "BKL1_GPIO");
Marcel Ziswiler475ceff2019-05-31 19:00:20 +0300197}
198#else
199static inline void board_gpio_init(void) {}
200#endif
201
202#if IS_ENABLED(CONFIG_FEC_MXC)
203#include <miiphy.h>
204
205int board_phy_config(struct phy_device *phydev)
206{
207 if (phydev->drv->config)
208 phydev->drv->config(phydev);
209
210 return 0;
211}
212#endif
213
Marcel Ziswilerd5ee5f82023-01-16 20:04:57 +0100214/*
215 * Backlight off before OS handover
216 */
217void board_preboot_os(void)
218{
219 gpio_direction_output(BKL1_GPIO, 0);
220}
221
Marcel Ziswiler475ceff2019-05-31 19:00:20 +0300222int checkboard(void)
223{
224 puts("Model: Toradex Apalis iMX8\n");
225
226 build_info();
227 print_bootinfo();
228
Marcel Ziswiler79d300c2023-01-16 20:04:56 +0100229 return 0;
230}
231
232static enum pcb_rev_t get_pcb_revision(void)
233{
234 unsigned int pcb_vers = 0;
235
236 imx8_iomux_setup_multiple_pads(pcb_vers_detect,
237 ARRAY_SIZE(pcb_vers_detect));
238
239 gpio_request(IMX_GPIO_NR(1, 18),
240 "PCB version detection on PAD SC_P_MIPI_DSI0_GPIO0_00");
241 gpio_request(IMX_GPIO_NR(1, 19),
242 "PCB version detection on PAD SC_P_MIPI_DSI0_GPIO0_01");
243 gpio_direction_input(IMX_GPIO_NR(1, 18));
244 gpio_direction_input(IMX_GPIO_NR(1, 19));
245
246 udelay(1000);
247
248 pcb_vers = gpio_get_value(IMX_GPIO_NR(1, 18));
249 pcb_vers |= gpio_get_value(IMX_GPIO_NR(1, 19)) << 1;
250
251 /* Set muxing back to default values for saving energy */
252 imx8_iomux_setup_multiple_pads(pcb_vers_default,
253 ARRAY_SIZE(pcb_vers_default));
254
255 switch (pcb_vers) {
256 case 0b11:
257 return PCB_VERSION_1_0;
258 case 0b10:
259 return PCB_VERSION_1_1;
260 default:
261 printf("Unknown PCB version=0x%x, default to V1.1\n", pcb_vers);
262 return PCB_VERSION_1_1;
263 }
264}
265
266static void select_dt_from_module_version(void)
267{
268 env_set("soc", "imx8qm");
269 env_set("variant", "-v1.1");
270
271 switch (tdx_hw_tag.prodid) {
272 /* Select Apalis iMX8QM device trees */
273 case APALIS_IMX8QM_IT:
274 case APALIS_IMX8QM_WIFI_BT_IT:
275 case APALIS_IMX8QM_8GB_WIFI_BT_IT:
276 if (get_pcb_revision() == PCB_VERSION_1_0)
277 env_set("variant", "");
278 break;
279 /* Select Apalis iMX8QP device trees */
280 case APALIS_IMX8QP_WIFI_BT:
281 case APALIS_IMX8QP:
282 env_set("soc", "imx8qp");
283 break;
284 default:
285 printf("Unknown Apalis iMX8 module\n");
286 return;
287 }
288}
289
290static int do_select_dt_from_module_version(struct cmd_tbl *cmdtp, int flag,
291 int argc, char * const argv[])
292{
293 select_dt_from_module_version();
Marcel Ziswiler475ceff2019-05-31 19:00:20 +0300294 return 0;
295}
296
Marcel Ziswiler79d300c2023-01-16 20:04:56 +0100297U_BOOT_CMD(select_dt_from_module_version, CONFIG_SYS_MAXARGS, 1, do_select_dt_from_module_version,
298 "\n", " - select devicetree from module version"
299);
300
Marcel Ziswiler475ceff2019-05-31 19:00:20 +0300301int board_init(void)
302{
303 board_gpio_init();
304
305 return 0;
306}
307
Marcel Ziswiler475ceff2019-05-31 19:00:20 +0300308/*
309 * Board specific reset that is system reset.
310 */
Harald Seiler6f14d5f2020-12-15 16:47:52 +0100311void reset_cpu(void)
Marcel Ziswiler475ceff2019-05-31 19:00:20 +0300312{
313 /* TODO */
314}
315
316#if defined(CONFIG_OF_LIBFDT) && defined(CONFIG_OF_BOARD_SETUP)
Masahiro Yamadaf7ed78b2020-06-26 15:13:33 +0900317int ft_board_setup(void *blob, struct bd_info *bd)
Marcel Ziswiler475ceff2019-05-31 19:00:20 +0300318{
319 return ft_common_board_setup(blob, bd);
320}
321#endif
322
323int board_mmc_get_env_dev(int devno)
324{
325 return devno;
326}
327
328int board_late_init(void)
329{
330#ifdef CONFIG_ENV_VARS_UBOOT_RUNTIME_CONFIG
331/* TODO move to common */
332 env_set("board_name", "Apalis iMX8QM");
333 env_set("board_rev", "v1.0");
334#endif
335
Marcel Ziswiler28c817f2023-01-16 20:04:58 +0100336 build_info();
337
Marcel Ziswiler79d300c2023-01-16 20:04:56 +0100338 select_dt_from_module_version();
339
Marcel Ziswiler475ceff2019-05-31 19:00:20 +0300340 return 0;
341}