summaryrefslogtreecommitdiff
path: root/zephyr/shim/chip/mchp/system_download_from_flash.c
blob: 51cdc5aab66e90e69a5d7400f74eb7487e552cd7 (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
/* Copyright 2022 The ChromiumOS Authors
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */
#include <zephyr/dt-bindings/clock/npcx_clock.h>
#include <stdnoreturn.h>

#include "common.h"
#include "soc.h"
#include "system_chip.h"

/* Modules Map */
#define WDT_NODE DT_INST(0, microchip_xec_watchdog)
#define STRUCT_WDT_REG_BASE_ADDR ((struct wdt_regs *)(DT_REG_ADDR(WDT_NODE)))

#define PCR_NODE DT_INST(0, microchip_xec_pcr)
#define STRUCT_PCR_REG_BASE_ADDR \
	((struct pcr_regs *)DT_REG_ADDR_BY_IDX(PCR_NODE, 0))

#define QSPI_NODE DT_INST(0, microchip_xec_qmspi_full_duplex)
#define STRUCT_QSPI_REG_BASE_ADDR \
	((struct qmspi_regs *)(DT_REG_ADDR(QSPI_NODE)))

#define SPI_READ_111 0x03
#define SPI_READ_111_FAST 0x0b
#define SPI_READ_112_FAST 0x3b

#define QSPI_STATUS_DONE (MCHP_QMSPI_STS_DONE | MCHP_QMSPI_STS_DMA_DONE)

#define QSPI_STATUS_ERR                                    \
	(MCHP_QMSPI_STS_TXB_ERR | MCHP_QMSPI_STS_RXB_ERR | \
	 MCHP_QMSPI_STS_PROG_ERR | MCHP_QMSPI_STS_LDMA_RX_ERR)

noreturn void __keep __attribute__((section(".code_in_sram2")))
__start_qspi(uint32_t resetVectAddr)
{
	struct pcr_regs *pcr = STRUCT_PCR_REG_BASE_ADDR;
	struct qmspi_regs *qspi = STRUCT_QSPI_REG_BASE_ADDR;
	struct wdt_regs *wdt = STRUCT_WDT_REG_BASE_ADDR;
	uint32_t qsts = 0;
	uint32_t exeAddr = 0;

	qspi->EXE = MCHP_QMSPI_EXE_START;

	qsts = qspi->STS;
	while (!(qsts & QSPI_STATUS_DONE)) {
		qsts = qspi->STS;
		if (qsts & QSPI_STATUS_ERR)
			break;
	}

	/* Stop the watchdog */
	wdt->CTRL &= ~MCHP_WDT_CTRL_EN;

	qspi->MODE &= ~(MCHP_QMSPI_M_ACTIVATE);
	if (qsts & QSPI_STATUS_ERR) {
		pcr->SYS_RST |= MCHP_PCR_SYS_RESET_NOW;
		while (1)
			;
	}

	/*
	 * Jump to the exeAddr address if needed. Setting bit 0 of address to
	 * indicate it's a thumb branch for cortex-m series CPU.
	 */
	exeAddr = *(uint32_t *)(resetVectAddr & ~0x03u);

	((void (*)(void))(exeAddr | 0x01))();

	/* Should never get here */
	while (1)
		;
}

uintptr_t __lfw_sram_start = CONFIG_CROS_EC_RAM_BASE + CONFIG_CROS_EC_RAM_SIZE;

typedef void (*START_QSPI_IN_SRAM_FP)(uint32_t);

void system_download_from_flash(uint32_t srcAddr, uint32_t dstAddr,
				uint32_t size, uint32_t resetVectAddr)
{
	struct pcr_regs *pcr = STRUCT_PCR_REG_BASE_ADDR;
	struct qmspi_regs *qspi = STRUCT_QSPI_REG_BASE_ADDR;
	uint32_t fdiv;
	uint32_t cmdaddr;
	int i;
	START_QSPI_IN_SRAM_FP __start_qspi_in_data_sram =
		(START_QSPI_IN_SRAM_FP)(__lfw_sram_start | 0x01);

	/* Check valid address for jumpiing */
	__ASSERT_NO_MSG(exeAddr != 0x0);
	/* Configure QMSPI controller */
	qspi->MODE = MCHP_QMSPI_M_SRST;
	fdiv = 2;
	if (pcr->TURBO_CLK & MCHP_PCR_TURBO_CLK_96M)
		fdiv *= 2;

	qspi->MODE = (fdiv << MCHP_QMSPI_M_FDIV_POS) & MCHP_QMSPI_M_FDIV_MASK;
	qspi->MODE |= (MCHP_QMSPI_M_ACTIVATE | MCHP_QMSPI_M_LDMA_RX_EN);
	qspi->CTRL = BIT(MCHP_QMSPI_C_DESCR_EN_POS);

	/* Transmit 4 bytes(opcode + 24-bit address) on IO0 */
	qspi->DESCR[0] =
		(MCHP_QMSPI_C_IFM_1X | MCHP_QMSPI_C_TX_DATA |
		 MCHP_QMSPI_C_XFR_UNITS_1 | MCHP_QMSPI_C_XFR_NUNITS(4) |
		 MCHP_QMSPI_C_NEXT_DESCR(1));

	/* Transmit 8 clocks with IO0 and IO1 tri-stated */
	qspi->DESCR[1] =
		(MCHP_QMSPI_C_IFM_2X | MCHP_QMSPI_C_TX_DIS |
		 MCHP_QMSPI_C_XFR_UNITS_1 | MCHP_QMSPI_C_XFR_NUNITS(2) |
		 MCHP_QMSPI_C_NEXT_DESCR(2));

	/* Read using LDMA RX Chan 0, IFM=2x, Last Descriptor, close */
	qspi->DESCR[2] = (MCHP_QMSPI_C_IFM_2X | MCHP_QMSPI_C_TX_DIS |
			  MCHP_QMSPI_C_RX_EN | MCHP_QMSPI_C_RX_DMA_1B |
			  MCHP_QMSPI_C_CLOSE | MCHP_QMSPI_C_DESCR_LAST |
			  MCHP_QMSPI_C_NEXT_DESCR(0));

	/* QSPI Local DMA RX channel 0 */
	qspi->LDMA_RX_DESCR_BM = BIT(2); /* descriptor 2 uses RX LDMA */
	qspi->LDRX[0].CTRL = MCHP_QMSPI_LDC_EN | MCHP_QMSPI_LDC_UCHL_EN |
			     MCHP_QMSPI_LDC_INCR_EN;
	qspi->LDRX[0].MSTART = dstAddr;
	qspi->LDRX[0].LEN = size;

	switch ((dstAddr | size) & 0x03u) {
	case 0:
		qspi->LDRX[0].CTRL |= MCHP_QMSPI_LDC_ASZ_4;
		break;
	case 2:
		qspi->LDRX[0].CTRL |= MCHP_QMSPI_LDC_ASZ_2;
		break;
	default:
		qspi->LDRX[0].CTRL |= MCHP_QMSPI_LDC_ASZ_1;
		break;
	}

	cmdaddr = __builtin_bswap32(srcAddr & 0x00ffffff);
	cmdaddr |= SPI_READ_112_FAST;

	qspi->TX_FIFO = cmdaddr;

	/* MCHP destination location */
	/* Copy the __start_gdma_in_lpram instructions to LPRAM */
	for (i = 0; i < &__flash_lplfw_end - &__flash_lplfw_start; i++) {
		*((uint32_t *)__lfw_sram_start + i) =
			*(&__flash_lplfw_start + i);
	}

	/* Call into SRAM routine to start QSPI */
	__start_qspi_in_data_sram(resetVectAddr);
}