summaryrefslogtreecommitdiffstats
path: root/arch/arm/mach-mxs/imx.c
blob: b7247b9b720a8d31241efc0bd28dfb1780b17b5f (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
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
/*
 * (C) Copyright 2010 Juergen Beisert - Pengutronix
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation; either version 2 of
 * the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 */

#include <common.h>
#include <bootsource.h>
#include <command.h>
#include <complete.h>
#include <init.h>
#include <io.h>
#include <stmp-device.h>

#include <mach/generic.h>
#include <mach/imx-regs.h>
#include <mach/revision.h>

#define HW_RTC_PERSISTENT1     0x070

static int imx_reset_usb_bootstrap(void)
{
	/*
	 * Clear USB boot mode.
	 *
	 * When the i.MX28 boots from USB, the ROM code sets this bit. When
	 * after a reset the ROM code detects that this bit is set it will
	 * boot from USB again. This means that if we boot once from USB the
	 * chip will continue to boot from USB until the next power cycle.
	 *
	 * To prevent this (and boot from the configured bootsource instead)
	 * clear this bit here.
	 */
	writel(0x2, IMX_WDT_BASE + HW_RTC_PERSISTENT1 + STMP_OFFSET_REG_CLR);

	return 0;
}
device_initcall(imx_reset_usb_bootstrap);

static int __silicon_revision = SILICON_REVISION_UNKNOWN;

int silicon_revision_get(void)
{
	return __silicon_revision;
}

void silicon_revision_set(const char *soc, int revision)
{
	__silicon_revision = revision;

	printf("detected %s revision %d.%d\n", soc,
	       (revision >> 4) & 0xf, revision & 0xf);
}

#define HW_DIGCTL_CHIPID	(0x8001c310)
#define HW_DIGCTL_CHIPID_MASK	(0xffff << 16)
#define HW_DIGCTL_CHIPID_MX23	(0x3780 << 16)
#define HW_DIGCTL_CHIPID_MX28	(0x2800 << 16)

static void mxs_silicon_revision(void)
{
	enum silicon_revision revision = SILICON_REVISION_UNKNOWN;
	const char *product = "unknown";
	uint32_t reg;
	uint8_t rev;

	reg = readl((void __iomem *)HW_DIGCTL_CHIPID);
	rev = reg & 0xff;

	switch (reg & HW_DIGCTL_CHIPID_MASK) {
	case HW_DIGCTL_CHIPID_MX23:
		product = "i.MX23";
		switch (rev) {
		case 0x0: revision = SILICON_REVISION_1_0; break;
		case 0x1: revision = SILICON_REVISION_1_1; break;
		case 0x2: revision = SILICON_REVISION_1_2; break;
		case 0x3: revision = SILICON_REVISION_1_3; break;
		case 0x4: revision = SILICON_REVISION_1_4; break;
		}
		break;
	case HW_DIGCTL_CHIPID_MX28:
		product = "i.MX28";
		switch (rev) {
		case 0x1: revision = SILICON_REVISION_1_2; break;
		}
	}

	silicon_revision_set(product, revision);
}

#define HW_PINCTRL_MUXSEL2 0x120
#define HW_PINCTRL_MUXSEL3 0x130
#define HW_PINCTRL_DOE1 0x710
#define HW_PINCTRL_DIN1 0x610

/*
 * we are interested in the setting of:
 * - GPIO BANK1/19: 1 = the ROM has used the following pins for boot source selection
 * - GPIO BANK1/5: ETM enable
 * - GPIO BANK1/3: BM3
 * - GPIO BANK1/2: BM2
 * - GPIO BANK1/1: BM1
 * - GPIO BANK1/0: BM0
 */
static uint32_t mx23_detect_bootsource(void)
{
	uint32_t mux2, mux3, dir, mode;

	mux3 = readl(IMX_IOMUXC_BASE + HW_PINCTRL_MUXSEL3);
	mux2 = readl(IMX_IOMUXC_BASE + HW_PINCTRL_MUXSEL2);
	dir = readl(IMX_IOMUXC_BASE + HW_PINCTRL_DOE1);

	/* force the GPIO lines of interest to input */
	writel(0x0008002f, IMX_IOMUXC_BASE + HW_PINCTRL_DOE1 + 8);
	/* force the GPIO lines of interest to act as GPIO */
	writel(0x00000cff, IMX_IOMUXC_BASE + HW_PINCTRL_MUXSEL2 + 4);
	writel(0x000000c0, IMX_IOMUXC_BASE + HW_PINCTRL_MUXSEL3 + 4);

	/* read the bootstrapping */
	mode = readl(IMX_IOMUXC_BASE + HW_PINCTRL_DIN1) & 0x8002f;

	/* restore previous settings */
	writel(mux3, IMX_IOMUXC_BASE + HW_PINCTRL_MUXSEL3);
	writel(mux2, IMX_IOMUXC_BASE + HW_PINCTRL_MUXSEL2);
	writel(dir, IMX_IOMUXC_BASE + HW_PINCTRL_DOE1);

	if (!(mode & (1 << 19)))
		return 0xff; /* invalid marker */

	return (mode & 0xf) | ((mode & 0x20) >> 1);
}

#define MX28_REV_1_0_MODE	(0x0001a7f0)
#define MX28_REV_1_2_MODE	(0x00019bf0)

static void mxs_boot_save_loc(void)
{
	enum bootsource src = BOOTSOURCE_UNKNOWN;
	int instance = 0;
	uint32_t mode = 0xff;

	if (cpu_is_mx23()) {
		mode = mx23_detect_bootsource();
	} else if (cpu_is_mx28()) {
		enum silicon_revision rev = silicon_revision_get();

		if (rev == SILICON_REVISION_1_2)
			mode = *(uint32_t *)MX28_REV_1_2_MODE;
		else
			mode = *(uint32_t *)MX28_REV_1_0_MODE;
	}

	switch (mode & 0xf) {
	case 0x0: src = BOOTSOURCE_USB; break;		/* "USB" */
	case 0x1: src = BOOTSOURCE_I2C_EEPROM; break;	/* "I2C, master" */
	case 0x3: instance = 1;	/* fallthrough */	/* "SSP SPI #2, master, NOR" */
	case 0x2: src = BOOTSOURCE_SPI_NOR; break;	/* "SSP SPI #1, master, NOR" */
	case 0x4: src = BOOTSOURCE_NAND; break;		/* "NAND" */
	case 0x8: src = BOOTSOURCE_SPI_EEPROM; break;	/* "SSP SPI #3, master, EEPROM" */
	case 0xa: instance = 1;	/* fallthrough */	/* "SSP SD/MMC #1" */
	case 0x9: src = BOOTSOURCE_MMC; break;		/* "SSP SD/MMC #0" */
	}

	bootsource_set(src);
	bootsource_set_instance(instance);
}

static int mxs_init(void)
{
	mxs_silicon_revision();
	mxs_boot_save_loc();

	return 0;
}
postcore_initcall(mxs_init);