]>
Commit | Line | Data |
---|---|---|
afbf8899 JR |
1 | /* |
2 | * Copyright (C) ST-Ericsson SA 2009 | |
3 | * | |
4 | * This program is free software; you can redistribute it and/or modify | |
5 | * it under the terms of the GNU General Public License as published by | |
6 | * the Free Software Foundation; either version 2 of the License, or | |
7 | * (at your option) any later version. | |
8 | * | |
9 | * This program is distributed in the hope that it will be useful, | |
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
12 | * GNU General Public License for more details. | |
13 | * | |
14 | * You should have received a copy of the GNU General Public License | |
15 | * along with this program; if not, write to the Free Software | |
16 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | |
17 | */ | |
18 | ||
19 | #include <config.h> | |
20 | #include <common.h> | |
10ed93dc | 21 | #include <malloc.h> |
afbf8899 JR |
22 | #include <i2c.h> |
23 | #include <asm/types.h> | |
24 | #include <asm/io.h> | |
25 | #include <asm/errno.h> | |
26 | #include <asm/arch/clock.h> | |
27 | #include <asm/arch/gpio.h> | |
28 | #include <asm/arch/hardware.h> | |
29 | #include <asm/arch/sys_proto.h> | |
42cb8fb6 | 30 | #include <asm/arch/prcmu.h> |
9652de7c | 31 | #ifdef CONFIG_MMC |
afbf8899 JR |
32 | #include "../../../drivers/mmc/arm_pl180_mmci.h" |
33 | #endif | |
34 | ||
35 | #define NOMADIK_PER4_BASE (0x80150000) | |
36 | #define NOMADIK_BACKUPRAM0_BASE (NOMADIK_PER4_BASE + 0x00000) | |
37 | #define NOMADIK_BACKUPRAM1_BASE (NOMADIK_PER4_BASE + 0x01000) | |
38 | ||
39 | /* Power, Reset, Clock Management Unit */ | |
40 | /* | |
41 | * SVA: Smart Video Accelerator | |
42 | * SIA: Smart Imaging Accelerator | |
43 | * SGA: Smart Graphic accelerator | |
44 | * B2R2: Graphic blitter | |
45 | */ | |
afbf8899 JR |
46 | #define PRCM_ARMCLKFIX_MGT_REG (PRCMU_BASE + 0x000) |
47 | #define PRCM_ACLK_MGT_REG (PRCMU_BASE + 0x004) | |
48 | #define PRCM_SVAMMDSPCLK_MGT_REG (PRCMU_BASE + 0x008) | |
49 | #define PRCM_SIAMMDSPCLK_MGT_REG (PRCMU_BASE + 0x00C) | |
50 | #define PRCM_SAAMMDSPCLK_MGT_REG (PRCMU_BASE + 0x010) | |
51 | #define PRCM_SGACLK_MGT_REG (PRCMU_BASE + 0x014) | |
52 | #define PRCM_UARTCLK_MGT_REG (PRCMU_BASE + 0x018) | |
53 | #define PRCM_MSPCLK_MGT_REG (PRCMU_BASE + 0x01C) | |
54 | #define PRCM_I2CCLK_MGT_REG (PRCMU_BASE + 0x020) | |
55 | #define PRCM_SDMMCCLK_MGT_REG (PRCMU_BASE + 0x024) | |
56 | #define PRCM_SLIMCLK_MGT_REG (PRCMU_BASE + 0x028) | |
57 | #define PRCM_PER1CLK_MGT_REG (PRCMU_BASE + 0x02C) | |
58 | #define PRCM_PER2CLK_MGT_REG (PRCMU_BASE + 0x030) | |
59 | #define PRCM_PER3CLK_MGT_REG (PRCMU_BASE + 0x034) | |
60 | #define PRCM_PER5CLK_MGT_REG (PRCMU_BASE + 0x038) | |
61 | #define PRCM_PER6CLK_MGT_REG (PRCMU_BASE + 0x03C) | |
62 | #define PRCM_PER7CLK_MGT_REG (PRCMU_BASE + 0x040) | |
63 | #define PRCM_DMACLK_MGT_REG (PRCMU_BASE + 0x074) | |
64 | #define PRCM_B2R2CLK_MGT_REG (PRCMU_BASE + 0x078) | |
65 | ||
66 | #define PRCM_PLLSOC0_FREQ_REG (PRCMU_BASE + 0x080) | |
67 | #define PRCM_PLLSOC1_FREQ_REG (PRCMU_BASE + 0x084) | |
68 | #define PRCM_PLLARM_FREQ_REG (PRCMU_BASE + 0x088) | |
69 | #define PRCM_PLLDDR_FREQ_REG (PRCMU_BASE + 0x08C) | |
70 | #define PRCM_ARM_CHGCLKREQ_REG (PRCMU_BASE + 0x114) | |
71 | ||
72 | #define PRCM_TCR (PRCMU_BASE + 0x1C8) | |
73 | ||
74 | /* | |
75 | * Memory controller register | |
76 | */ | |
77 | #define DMC_BASE_ADDR 0x80156000 | |
78 | #define DMC_CTL_97 (DMC_BASE_ADDR + 0x184) | |
79 | ||
80 | int board_id; /* set in board_late_init() */ | |
81 | ||
82 | /* PLLs for clock management registers */ | |
83 | enum { | |
84 | GATED = 0, | |
85 | PLLSOC0, /* pllsw = 001, ffs() = 1 */ | |
86 | PLLSOC1, /* pllsw = 010, ffs() = 2 */ | |
87 | PLLDDR, /* pllsw = 100, ffs() = 3 */ | |
88 | PLLARM, | |
89 | }; | |
90 | ||
91 | static struct pll_freq_regs { | |
92 | int idx; /* index fror pll_name and pll_khz arrays */ | |
93 | uint32_t addr; | |
94 | } pll_freq_regs[] = { | |
95 | {PLLSOC0, PRCM_PLLSOC0_FREQ_REG}, | |
96 | {PLLSOC1, PRCM_PLLSOC1_FREQ_REG}, | |
97 | {PLLDDR, PRCM_PLLDDR_FREQ_REG}, | |
98 | {PLLARM, PRCM_PLLARM_FREQ_REG}, | |
99 | {0, 0}, | |
100 | }; | |
101 | ||
102 | static const char *pll_name[5] = {"GATED", "SOC0", "SOC1", "DDR", "ARM"}; | |
103 | static uint32_t pll_khz[5]; /* use ffs(pllsw(reg)) as index for 0..3 */ | |
104 | ||
105 | static struct clk_mgt_regs { | |
106 | uint32_t addr; | |
107 | uint32_t val; | |
108 | const char *descr; | |
109 | } clk_mgt_regs[] = { | |
110 | /* register content taken from bootrom settings */ | |
111 | {PRCM_ARMCLKFIX_MGT_REG, 0x0120, "ARMCLKFIX"}, /* ena, SOC0/0, ??? */ | |
112 | {PRCM_ACLK_MGT_REG, 0x0125, "ACLK"}, /* ena, SOC0/5, 160 MHz */ | |
113 | {PRCM_SVAMMDSPCLK_MGT_REG, 0x1122, "SVA"}, /* ena, SOC0/2, 400 MHz */ | |
114 | {PRCM_SIAMMDSPCLK_MGT_REG, 0x0022, "SIA"}, /* dis, SOC0/2, 400 MHz */ | |
115 | {PRCM_SAAMMDSPCLK_MGT_REG, 0x0822, "SAA"}, /* dis, SOC0/4, 200 MHz */ | |
116 | {PRCM_SGACLK_MGT_REG, 0x0024, "SGA"}, /* dis, SOC0/4, 200 MHz */ | |
117 | {PRCM_UARTCLK_MGT_REG, 0x0300, "UART"}, /* ena, GATED, CLK38 */ | |
118 | {PRCM_MSPCLK_MGT_REG, 0x0200, "MSP"}, /* dis, GATED, CLK38 */ | |
119 | {PRCM_I2CCLK_MGT_REG, 0x0130, "I2C"}, /* ena, SOC0/16, 50 MHz */ | |
120 | {PRCM_SDMMCCLK_MGT_REG, 0x0130, "SDMMC"}, /* ena, SOC0/16, 50 MHz */ | |
121 | {PRCM_PER1CLK_MGT_REG, 0x126, "PER1"}, /* ena, SOC0/6, 133 MHz */ | |
122 | {PRCM_PER2CLK_MGT_REG, 0x126, "PER2"}, /* ena, SOC0/6, 133 MHz */ | |
123 | {PRCM_PER3CLK_MGT_REG, 0x126, "PER3"}, /* ena, SOC0/6, 133 MHz */ | |
124 | {PRCM_PER5CLK_MGT_REG, 0x126, "PER5"}, /* ena, SOC0/6, 133 MHz */ | |
125 | {PRCM_PER6CLK_MGT_REG, 0x126, "PER6"}, /* ena, SOC0/6, 133 MHz */ | |
126 | {PRCM_PER7CLK_MGT_REG, 0x128, "PER7"}, /* ena, SOC0/8, 100 MHz */ | |
127 | {PRCM_DMACLK_MGT_REG, 0x125, "DMA"}, /* ena, SOC0/5, 160 MHz */ | |
128 | {PRCM_B2R2CLK_MGT_REG, 0x025, "B2R2"}, /* dis, SOC0/5, 160 MHz */ | |
129 | {0, 0, NULL}, | |
130 | }; | |
131 | ||
132 | static void init_regs(void); | |
133 | ||
134 | DECLARE_GLOBAL_DATA_PTR; | |
135 | #if defined(CONFIG_SHOW_BOOT_PROGRESS) | |
136 | void show_boot_progress(int progress) | |
137 | { | |
138 | printf("Boot reached stage %d\n", progress); | |
139 | } | |
140 | #endif | |
141 | ||
afbf8899 JR |
142 | /* |
143 | * Miscellaneous platform dependent initialisations | |
144 | */ | |
145 | ||
146 | int board_early_init_f(void) | |
147 | { | |
148 | init_regs(); | |
149 | return 0; | |
150 | } | |
151 | ||
152 | int board_init(void) | |
153 | { | |
154 | uint32_t unused_cols_rows; | |
155 | unsigned int nrows; | |
156 | unsigned int ncols; | |
157 | ||
158 | gd->bd->bi_arch_number = 0x1A4; | |
159 | gd->bd->bi_boot_params = 0x00000100; | |
160 | gd->bd->bi_dram[0].start = CONFIG_SYS_SDRAM_BASE; | |
161 | ||
162 | /* | |
163 | * Assumption: 2 CS active, both CS have same layout. | |
164 | * 15 rows max, 11 cols max (controller spec). | |
165 | * memory chip has 8 banks, I/O width 32 bit. | |
166 | * The correct way would be to read MR#8: I/O width and density, | |
167 | * but this requires locking against the PRCMU firmware. | |
168 | * Simplified approach: | |
169 | * Read number of unused rows and columns from mem controller. | |
170 | * size = nCS x 2^(rows+cols) x nbanks x buswidth_bytes | |
171 | */ | |
172 | unused_cols_rows = readl(DMC_CTL_97); | |
173 | nrows = 15 - (unused_cols_rows & 0x07); | |
174 | ncols = 11 - ((unused_cols_rows & 0x0700) >> 8); | |
175 | gd->bd->bi_dram[0].size = 2 * (1 << (nrows + ncols)) * 8 * 4; | |
176 | ||
177 | icache_enable(); | |
178 | ||
179 | return 0; | |
180 | } | |
181 | ||
182 | int dram_init(void) | |
183 | { | |
184 | gd->ram_size = PHYS_SDRAM_SIZE_1; | |
185 | ||
186 | return 0; | |
187 | } | |
188 | ||
189 | unsigned int addr_vall_arr[] = { | |
190 | 0x8011F000, 0x0000FFFF, /* Clocks for HSI TODO: Enable reqd only */ | |
191 | 0x8011F008, 0x00001CFF, /* Clocks for HSI TODO: Enable reqd only */ | |
192 | 0x8000F000, 0x00007FFF, /* Clocks for I2C TODO: Enable reqd only */ | |
193 | 0x8000F008, 0x00007FFF, /* Clocks for I2C TODO: Enable reqd only */ | |
194 | 0x80157020, 0x00000150, /* I2C 48MHz clock */ | |
195 | 0x8012F000, 0x00007FFF, /* Clocks for SD TODO: Enable reqd only */ | |
196 | 0x8012F008, 0x00007FFF, /* Clocks for SD TODO: Enable reqd only */ | |
197 | 0xA03DF000, 0x0000000D, /* Clock for MTU Timers */ | |
198 | 0x8011E00C, 0x00000000, /* GPIO ALT FUNC for EMMC */ | |
199 | 0x8011E004, 0x0000FFE0, /* GPIO ALT FUNC for EMMC */ | |
200 | 0x8011E020, 0x0000FFE0, /* GPIO ALT FUNC for EMMC */ | |
201 | 0x8011E024, 0x00000000, /* GPIO ALT FUNC for EMMC */ | |
202 | 0x8012E000, 0x20000000, /* GPIO ALT FUNC for UART */ | |
203 | 0x8012E00C, 0x00000000, /* GPIO ALT FUNC for SD */ | |
204 | 0x8012E004, 0x0FFC0000, /* GPIO ALT FUNC for SD */ | |
205 | 0x8012E020, 0x60000000, /* GPIO ALT FUNC for SD */ | |
206 | 0x8012E024, 0x60000000, /* GPIO ALT FUNC for SD */ | |
207 | 0x801571E4, 0x0000000C, /* PRCMU settings for B2R2, | |
208 | PRCM_APE_RESETN_SET_REG */ | |
209 | 0x80157024, 0x00000130, /* PRCMU settings for EMMC/SD */ | |
210 | 0xA03FF000, 0x00000003, /* USB */ | |
211 | 0xA03FF008, 0x00000001, /* USB */ | |
212 | 0xA03FE00C, 0x00000000, /* USB */ | |
213 | 0xA03FE020, 0x00000FFF, /* USB */ | |
214 | 0xA03FE024, 0x00000000 /* USB */ | |
215 | }; | |
216 | ||
9660e442 | 217 | #ifdef CONFIG_BOARD_LATE_INIT |
afbf8899 JR |
218 | /* |
219 | * called after all initialisation were done, but before the generic | |
220 | * mmc_initialize(). | |
221 | */ | |
222 | int board_late_init(void) | |
223 | { | |
224 | uchar byte; | |
225 | ||
226 | /* | |
227 | * Determine and set board_id environment variable | |
228 | * 0: mop500, 1: href500 | |
229 | * Above boards have different GPIO expander chips which we can | |
230 | * distinguish by the chip id. | |
231 | * | |
232 | * The board_id environment variable is needed for the Linux bootargs. | |
233 | */ | |
234 | (void) i2c_set_bus_num(0); | |
235 | (void) i2c_read(CONFIG_SYS_I2C_GPIOE_ADDR, 0x80, 1, &byte, 1); | |
236 | if (byte == 0x01) { | |
237 | board_id = 0; | |
238 | setenv("board_id", "0"); | |
239 | } else { | |
240 | board_id = 1; | |
241 | setenv("board_id", "1"); | |
242 | } | |
243 | #ifdef CONFIG_MMC | |
1e37322e | 244 | u8500_mmc_power_init(); |
afbf8899 JR |
245 | |
246 | /* | |
247 | * config extended GPIO pins for level shifter and | |
248 | * SDMMC_ENABLE | |
249 | */ | |
250 | if (board_id == 0) { | |
251 | /* MOP500 */ | |
252 | byte = 0x0c; | |
253 | (void) i2c_write(CONFIG_SYS_I2C_GPIOE_ADDR, 0x89, 1, &byte, 1); | |
254 | (void) i2c_write(CONFIG_SYS_I2C_GPIOE_ADDR, 0x83, 1, &byte, 1); | |
255 | } else { | |
256 | /* HREF */ | |
257 | /* set the direction of GPIO KPY9 and KPY10 */ | |
258 | byte = 0x06; | |
259 | (void) i2c_write(CONFIG_SYS_I2C_GPIOE_ADDR, 0xC8, 1, &byte, 1); | |
260 | /* must be a multibyte access */ | |
261 | (void) i2c_write(CONFIG_SYS_I2C_GPIOE_ADDR, 0xC4, 1, | |
262 | (uchar []) {0x06, 0x06}, 2); | |
263 | } | |
264 | #endif /* CONFIG_MMC */ | |
265 | /* | |
266 | * Create a memargs variable which points uses either the memargs256 or | |
267 | * memargs512 environment variable, depending on the memory size. | |
268 | * memargs is used to build the bootargs, memargs256 and memargs512 are | |
269 | * stored in the environment. | |
270 | */ | |
271 | if (gd->bd->bi_dram[0].size == 0x10000000) { | |
272 | setenv("memargs", "setenv bootargs ${bootargs} ${memargs256}"); | |
273 | setenv("mem", "256M"); | |
274 | } else { | |
275 | setenv("memargs", "setenv bootargs ${bootargs} ${memargs512}"); | |
276 | setenv("mem", "512M"); | |
277 | } | |
278 | ||
279 | return 0; | |
280 | } | |
9660e442 | 281 | #endif /* CONFIG_BOARD_LATE_INIT */ |
afbf8899 JR |
282 | |
283 | static void early_gpio_setup(struct gpio_register *gpio_reg, u32 bits) | |
284 | { | |
285 | writel(readl(&gpio_reg->gpio_dats) | bits, &gpio_reg->gpio_dats); | |
286 | writel(readl(&gpio_reg->gpio_pdis) & ~bits, &gpio_reg->gpio_pdis); | |
287 | } | |
288 | ||
289 | static void init_regs(void) | |
290 | { | |
291 | /* FIXME Remove magic register array settings for ED also */ | |
292 | struct prcmu *prcmu = (struct prcmu *) U8500_PRCMU_BASE; | |
293 | ||
294 | /* Enable timers */ | |
295 | writel(1 << 17, &prcmu->tcr); | |
296 | ||
297 | u8500_prcmu_enable(&prcmu->per1clk_mgt); | |
298 | u8500_prcmu_enable(&prcmu->per2clk_mgt); | |
299 | u8500_prcmu_enable(&prcmu->per3clk_mgt); | |
300 | u8500_prcmu_enable(&prcmu->per5clk_mgt); | |
301 | u8500_prcmu_enable(&prcmu->per6clk_mgt); | |
302 | u8500_prcmu_enable(&prcmu->per7clk_mgt); | |
303 | ||
304 | u8500_prcmu_enable(&prcmu->uartclk_mgt); | |
305 | u8500_prcmu_enable(&prcmu->i2cclk_mgt); | |
306 | ||
307 | u8500_prcmu_enable(&prcmu->sdmmcclk_mgt); | |
308 | ||
309 | u8500_clock_enable(1, 9, -1); /* GPIO0 */ | |
310 | ||
311 | u8500_clock_enable(2, 11, -1); /* GPIO1 */ | |
312 | ||
313 | u8500_clock_enable(3, 8, -1); /* GPIO2 */ | |
314 | u8500_clock_enable(5, 1, -1); /* GPIO3 */ | |
315 | ||
316 | u8500_clock_enable(3, 6, 6); /* UART2 */ | |
317 | ||
318 | gpio_altfuncenable(GPIO_ALT_I2C_0, "I2C0"); | |
319 | u8500_clock_enable(3, 3, 3); /* I2C0 */ | |
320 | ||
321 | early_gpio_setup((struct gpio_register *)U8500_GPIO_0_BASE, 0x60000000); | |
322 | gpio_altfuncenable(GPIO_ALT_UART_2, "UART2"); | |
323 | ||
324 | early_gpio_setup((struct gpio_register *)U8500_GPIO_6_BASE, 0x0000ffe0); | |
325 | gpio_altfuncenable(GPIO_ALT_EMMC, "EMMC"); | |
326 | ||
327 | early_gpio_setup((struct gpio_register *)U8500_GPIO_0_BASE, 0x0000ffe0); | |
328 | gpio_altfuncenable(GPIO_ALT_SD_CARD0, "SDCARD"); | |
329 | ||
330 | u8500_clock_enable(1, 5, 5); /* SDI0 */ | |
331 | u8500_clock_enable(2, 4, 2); /* SDI4 */ | |
332 | ||
333 | u8500_clock_enable(6, 7, -1); /* MTU0 */ | |
334 | u8500_clock_enable(3, 4, 4); /* SDI2 */ | |
335 | ||
336 | early_gpio_setup((struct gpio_register *)U8500_GPIO_4_BASE, 0x000007ff); | |
337 | gpio_altfuncenable(GPIO_ALT_POP_EMMC, "EMMC"); | |
338 | ||
339 | /* | |
340 | * Enabling clocks for all devices which are AMBA devices in the | |
341 | * kernel. Otherwise they will not get probe()'d because the | |
342 | * peripheral ID register will not be powered. | |
343 | */ | |
344 | ||
345 | /* XXX: some of these differ between ED/V1 */ | |
346 | ||
347 | u8500_clock_enable(1, 1, 1); /* UART1 */ | |
348 | u8500_clock_enable(1, 0, 0); /* UART0 */ | |
349 | ||
350 | u8500_clock_enable(3, 2, 2); /* SSP1 */ | |
351 | u8500_clock_enable(3, 1, 1); /* SSP0 */ | |
352 | ||
353 | u8500_clock_enable(2, 8, -1); /* SPI0 */ | |
354 | u8500_clock_enable(2, 5, 3); /* MSP2 */ | |
355 | } | |
356 | ||
357 | #ifdef CONFIG_MMC | |
358 | static int u8500_mmci_board_init(void) | |
359 | { | |
360 | enum gpio_error error; | |
361 | struct gpio_register *gpio_base_address; | |
362 | ||
363 | gpio_base_address = (void *)(U8500_GPIO_0_BASE); | |
364 | gpio_base_address->gpio_dats |= 0xFFC0000; | |
365 | gpio_base_address->gpio_pdis &= ~0xFFC0000; | |
366 | ||
367 | /* save the GPIO0 AFSELA register */ | |
368 | error = gpio_altfuncenable(GPIO_ALT_SD_CARD0, "MMC"); | |
369 | if (error != GPIO_OK) { | |
370 | printf("u8500_mmci_board_init() gpio_altfuncenable failed\n"); | |
371 | return -ENODEV; | |
372 | } | |
373 | return 0; | |
374 | } | |
375 | ||
376 | int board_mmc_init(bd_t *bd) | |
377 | { | |
10ed93dc JR |
378 | struct pl180_mmc_host *host; |
379 | ||
afbf8899 JR |
380 | if (u8500_mmci_board_init()) |
381 | return -ENODEV; | |
382 | ||
10ed93dc JR |
383 | host = malloc(sizeof(struct pl180_mmc_host)); |
384 | if (!host) | |
385 | return -ENOMEM; | |
386 | memset(host, 0, sizeof(*host)); | |
387 | ||
388 | strcpy(host->name, "MMC"); | |
389 | host->base = (struct sdi_registers *)CONFIG_ARM_PL180_MMCI_BASE; | |
390 | host->pwr_init = INIT_PWR; | |
391 | host->clkdiv_init = SDI_CLKCR_CLKDIV_INIT_V1 | SDI_CLKCR_CLKEN; | |
392 | host->voltages = VOLTAGE_WINDOW_MMC; | |
393 | host->caps = 0; | |
394 | host->clock_in = ARM_MCLK; | |
395 | host->clock_min = ARM_MCLK / (2 * (SDI_CLKCR_CLKDIV_INIT_V1 + 1)); | |
396 | host->clock_max = CONFIG_ARM_PL180_MMCI_CLOCK_FREQ; | |
397 | ||
398 | return arm_pl180_mmci_init(host); | |
afbf8899 JR |
399 | } |
400 | #endif | |
401 | ||
402 | ||
403 | /* | |
404 | * get_pll_freq_khz - return PLL frequency in kHz | |
405 | */ | |
406 | static uint32_t get_pll_freq_khz(uint32_t inclk_khz, uint32_t freq_reg) | |
407 | { | |
408 | uint32_t idf, ldf, odf, seldiv, phi; | |
409 | ||
410 | /* | |
411 | * PLLOUTCLK = PHI = (INCLK*LDF)/(2*ODF*IDF) if SELDIV2=0 | |
412 | * PLLOUTCLK = PHI = (INCLK*LDF)/(4*ODF*IDF) if SELDIV2=1 | |
413 | * where: | |
414 | * IDF=R(2:0) (when R=000, IDF=1d) | |
415 | * LDF = 2*D(7:0) (D must be greater than or equal to 6) | |
416 | * ODF = N(5:0) (when N=000000, 0DF=1d) | |
417 | */ | |
418 | ||
419 | idf = (freq_reg & 0x70000) >> 16; | |
420 | ldf = (freq_reg & 0xff) * 2; | |
421 | odf = (freq_reg & 0x3f00) >> 8; | |
422 | seldiv = (freq_reg & 0x01000000) >> 24; | |
423 | phi = (inclk_khz * ldf) / (2 * odf * idf); | |
424 | if (seldiv) | |
425 | phi = phi/2; | |
426 | ||
427 | return phi; | |
428 | } | |
429 | ||
430 | int do_clkinfo(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) | |
431 | { | |
432 | uint32_t inclk_khz; | |
433 | uint32_t reg, phi; | |
434 | uint32_t clk_khz; | |
435 | unsigned int clk_sel; | |
436 | struct clk_mgt_regs *clks = clk_mgt_regs; | |
437 | struct pll_freq_regs *plls = pll_freq_regs; | |
438 | ||
439 | /* | |
440 | * Go through list of PLLs. | |
441 | * Initialise pll out frequency array (pll_khz) and print frequency. | |
442 | */ | |
443 | inclk_khz = 38400; /* 38.4 MHz */ | |
444 | while (plls->addr) { | |
445 | reg = readl(plls->addr); | |
446 | phi = get_pll_freq_khz(inclk_khz, reg); | |
447 | pll_khz[plls->idx] = phi; | |
448 | printf("%s PLL out frequency: %d.%d Mhz\n", | |
449 | pll_name[plls->idx], phi/1000, phi % 1000); | |
450 | plls++; | |
451 | } | |
452 | ||
453 | /* check ARM clock source */ | |
454 | reg = readl(PRCM_ARM_CHGCLKREQ_REG); | |
455 | printf("A9 running on %s\n", | |
456 | (reg & 1) ? "external clock" : "ARM PLL"); | |
457 | ||
458 | /* go through list of clk_mgt_reg */ | |
459 | printf("\n%19s %9s %7s %9s enabled\n", | |
460 | "name(addr)", "value", "PLL", "CLK[MHz]"); | |
461 | while (clks->addr) { | |
462 | reg = readl(clks->addr); | |
463 | ||
464 | /* convert bit position into array index */ | |
465 | clk_sel = ffs((reg >> 5) & 0x7); /* PLLSW[2:0] */ | |
466 | ||
467 | if (reg & 0x200) | |
468 | clk_khz = 38400; /* CLK38 is set */ | |
469 | else if ((reg & 0x1f) == 0) | |
470 | /* ARMCLKFIX_MGT is 0x120, e.g. div = 0 ! */ | |
471 | clk_khz = 0; | |
472 | else | |
473 | clk_khz = pll_khz[clk_sel] / (reg & 0x1f); | |
474 | ||
475 | printf("%9s(%08x): %08x, %6s, %4d.%03d, %s\n", | |
476 | clks->descr, clks->addr, reg, pll_name[clk_sel], | |
477 | clk_khz / 1000, clk_khz % 1000, | |
478 | (reg & 0x100) ? "ena" : "dis"); | |
479 | clks++; | |
480 | } | |
481 | ||
482 | return 0; | |
483 | } | |
484 | ||
485 | U_BOOT_CMD( | |
486 | clkinfo, 1, 1, do_clkinfo, | |
487 | "print clock info", | |
488 | "" | |
489 | ); |