]> git.kernelconcepts.de Git - karo-tx-redboot.git/blob - packages/hal/arm/mx25/var/v2_0/src/cmds.c
Merge branch 'master' of git+ssh://git.kernelconcepts.de/karo-tx-redboot
[karo-tx-redboot.git] / packages / hal / arm / mx25 / var / v2_0 / src / cmds.c
1 //==========================================================================
2 //
3 //              cmds.c
4 //
5 //              SoC [platform] specific RedBoot commands
6 //
7 //==========================================================================
8 //####ECOSGPLCOPYRIGHTBEGIN####
9 // -------------------------------------------
10 // This file is part of eCos, the Embedded Configurable Operating System.
11 // Copyright (C) 1998, 1999, 2000, 2001, 2002 Red Hat, Inc.
12 //
13 // eCos is free software; you can redistribute it and/or modify it under
14 // the terms of the GNU General Public License as published by the Free
15 // Software Foundation; either version 2 or (at your option) any later version.
16 //
17 // eCos is distributed in the hope that it will be useful, but WITHOUT ANY
18 // WARRANTY; without even the implied warranty of MERCHANTABILITY or
19 // FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
20 // for more details.
21 //
22 // You should have received a copy of the GNU General Public License along
23 // with eCos; if not, write to the Free Software Foundation, Inc.,
24 // 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
25 //
26 // As a special exception, if other files instantiate templates or use macros
27 // or inline functions from this file, or you compile this file and link it
28 // with other works to produce a work based on this file, this file does not
29 // by itself cause the resulting work to be covered by the GNU General Public
30 // License. However the source code for this file must still be made available
31 // in accordance with section (3) of the GNU General Public License.
32 //
33 // This exception does not invalidate any other reasons why a work based on
34 // this file might be covered by the GNU General Public License.
35 //
36 // Alternative licenses for eCos may be arranged by contacting Red Hat, Inc.
37 // at http://sources.redhat.com/ecos/ecos-license/
38 // -------------------------------------------
39 //####ECOSGPLCOPYRIGHTEND####
40 //==========================================================================
41 #include <redboot.h>
42 #include <cyg/hal/hal_intr.h>
43 #include <cyg/hal/plf_mmap.h>
44 #include <cyg/hal/hal_soc.h>            // Hardware definitions
45 #include <cyg/hal/hal_cache.h>
46
47 #define IIM_FUSE_DEBUG
48
49 typedef unsigned long long      u64;
50 typedef unsigned int            u32;
51 typedef unsigned short          u16;
52 typedef unsigned char           u8;
53
54 u32 pll_clock(enum plls pll);
55 u32 get_main_clock(enum main_clocks clk);
56 u32 get_peri_clock(enum peri_clocks clk);
57
58 static void clock_setup(int argc, char *argv[]);
59
60 RedBoot_cmd("clock",
61                         "Setup/Display clock\nSyntax:",
62                         "[<ARM core clock in MHz> [:<ARM-AHB clock divider>]\n\
63 If a selection is zero or no divider is specified, the optimal divider values\n\
64 will be chosen. Examples:\n\
65    [clock]         -> Show various clocks\n\
66    [clock 399]     -> Core=399   AHB=133           IPG=66.5(AHB/2)\n\
67    [clock 532:4]   -> Core=532   AHB=133(Core/4)   IPG=66.5(AHB/2)\n\
68    [clock 399:4]   -> Core=399   AHB=99.75(Core/4) IPG=49.875(AHB/2)\n\
69    [clock 199:3]   -> Core=199.5 AHB=66.5(Core/3)  IPG=33.25(AHB/2)\n\
70    [clock 133:2]   -> Core=133   AHB=66.5(Core/2)  IPG=33.25(AHB/2)\n\
71                       Core range: 532-133, AHB range: 133-66.5, IPG is always AHB/2\n",
72                         clock_setup
73                    );
74
75 void clock_spi_enable(unsigned int spi_clk)
76 {
77         diag_printf("%s: stubbed\n", __func__);
78 }
79
80 static void clock_setup(int argc,char *argv[])
81 {
82         u32 i, data[2], core_clk, ahb_div, cctl, arm_src, arm_div;
83                 unsigned long temp;
84
85         if (argc == 1)
86                 goto print_clock;
87
88         for (i = 0;     i < 2; i++) {
89                 if (!parse_num(argv[1], &temp, &argv[1], ":")) {
90                         diag_printf("Error: Invalid parameter\n");
91                         return;
92                 }
93                 data[i] = temp;
94         }
95
96         core_clk = data[0];
97         ahb_div = data[1] - 1;
98
99         if (core_clk / (ahb_div + 1) > 133 ||
100                 core_clk / (ahb_div + 1) < 66) {
101                 diag_printf("Illegal AHB divider value specified\n");
102                 return;
103         }
104
105         switch (core_clk) {
106         case 532:
107                 arm_src = 0;
108                 arm_div = 1 - 1;
109                 break;
110
111         case 399:
112                 arm_src = 1;
113                 arm_div = 1 - 1;
114                 break;
115
116         case 199:
117         case 200:
118                 arm_src = 1;
119                 arm_div = 2 - 1;
120                 break;
121
122         case 133:
123                 arm_src = 1;
124                 arm_div = 3 - 1;
125                 break;
126
127         default:
128                 diag_printf("Illegal core clock value specified\n");
129                 return;
130         }
131
132         cyg_hal_plf_serial_stop();
133
134         cctl = readl(CCM_BASE_ADDR + CLKCTL_CCTL);
135         cctl &= ~0xF0004000;
136         cctl |= arm_div << 30;
137         cctl |= ahb_div << 28;
138         cctl |= arm_src << 14;
139         writel(cctl, CCM_BASE_ADDR + CLKCTL_CCTL);
140
141         hal_delay_us(10000);
142         cyg_hal_plf_serial_init();
143
144         diag_printf("\n<<<New clock settings>>>\n");
145
146         // Now printing clocks
147 print_clock:
148         diag_printf("\nMPLL\t\tUPLL\n");
149         diag_printf("=========================\n");
150         diag_printf("%-16d%-16d\n\n", pll_clock(MCU_PLL), pll_clock(USB_PLL));
151         diag_printf("CPU\t\tAHB\t\tIPG\n");
152         diag_printf("========================================\n");
153         diag_printf("%-16d%-16d%-16d\n\n",
154                                 get_main_clock(CPU_CLK),
155                                 get_main_clock(AHB_CLK),
156                                 get_main_clock(IPG_CLK));
157
158         diag_printf("UART\n");
159         diag_printf("========\n");
160         diag_printf("%-16d\n\n",
161                                 get_peri_clock(PER_UART_CLK));
162
163         diag_printf("SPI\n");
164         diag_printf("========\n");
165         diag_printf("%-16d\n\n",
166                                 get_peri_clock(SPI1_CLK));
167 }
168
169 /*!
170  * This function returns the PLL output value in Hz based on pll.
171  */
172 u32 pll_clock(enum plls pll)
173 {
174         int mfi, mfn, mfd, pdf;
175         u32 pll_out;
176         u32 reg = readl(pll);
177         u64 ref_clk;
178
179         pdf = (reg >> 26) & 0xF;
180         mfd = (reg >> 16) & 0x3FF;
181         mfi = (reg >> 10) & 0xF;
182         if (mfi < 5) {
183                 mfi = 5;
184         }
185         mfn = reg & 0x3FF;
186         if (mfn >= 512) {
187                 mfn = 1024 - mfn;
188         }
189         ref_clk = PLL_REF_CLK;
190
191         pll_out = (2 * ref_clk * mfi + ((2 * ref_clk * mfn) / (mfd + 1))) /
192                 (pdf + 1);
193         return pll_out;
194 }
195
196 /*!
197  * This function returns the main clock value in Hz.
198  */
199 u32 get_main_clock(enum main_clocks clk)
200 {
201         u32 cctl = readl(CCM_BASE_ADDR + CLKCTL_CCTL);
202         u32 div;
203         u32 ret_val = 0;
204
205         switch (clk) {
206         case CPU_CLK:
207                 ret_val = pll_clock(MCU_PLL);
208                 if (cctl & CRM_CCTL_ARM_SRC) {
209                         ret_val = (ret_val * 3) / 4;
210                 }
211                 div = ((cctl >> CRM_CCTL_ARM_OFFSET) & 3) + 1;
212                 ret_val /= div;
213                 break;
214
215         case AHB_CLK:
216                 div = ((cctl >> CRM_CCTL_AHB_OFFSET) & 3) + 1;
217                 ret_val = get_main_clock(CPU_CLK) / div;
218                 break;
219
220         case IPG_CLK:
221         case IPG_PER_CLK:
222                 ret_val = get_main_clock(AHB_CLK) / 2;
223                 break;
224
225         default:
226                 diag_printf("Unknown clock: %d\n", clk);
227         }
228
229         return ret_val;
230 }
231
232 /*!
233  * This function returns the peripheral clock value in Hz.
234  */
235 u32 get_peri_clock(enum peri_clocks clk)
236 {
237         u32 ret_val = 0;
238         u32 pcdr, div;
239
240         switch (clk) {
241         case PER_UART_CLK:
242                 pcdr = readl(CCM_BASE_ADDR + CLKCTL_PCDR3);
243                 div = (pcdr >> 24) + 1;
244                 ret_val = get_main_clock(AHB_CLK) / div;
245                 break;
246
247         case SPI1_CLK:
248         case SPI2_CLK:
249                 ret_val = get_main_clock(IPG_CLK);
250                 break;
251
252         case LCDC_CLK:
253                 writel(readl(CCM_BASE_ADDR + CLKCTL_MCR) | (1 << 7),
254                         CCM_BASE_ADDR + CLKCTL_MCR);
255                 pcdr = readl(CCM_BASE_ADDR + CLKCTL_PCDR1);
256                 pcdr &= ~(0xff << 24);
257                 writel(pcdr, CCM_BASE_ADDR + CLKCTL_PCDR1);
258                 div = (pcdr >> 24) + 1;
259                 if (readl(CCM_BASE_ADDR + CLKCTL_MCR) & (1 << 7)) {
260                         ret_val = pll_clock(USB_PLL) / div;
261                 } else {
262                         ret_val = get_main_clock(AHB_CLK) / div;
263                 }
264                 break;
265
266         default:
267                 diag_printf("%s(): This clock: %d not supported yet\n",
268                                         __FUNCTION__, clk);
269         }
270         return ret_val;
271 }
272
273
274 #define IIM_ERR_SHIFT           8
275 #define POLL_FUSE_PRGD          (IIM_STAT_PRGD | (IIM_ERR_PRGE << IIM_ERR_SHIFT))
276 #define POLL_FUSE_SNSD          (IIM_STAT_SNSD | (IIM_ERR_SNSE << IIM_ERR_SHIFT))
277
278 static void fuse_op_start(void)
279 {
280         /* Do not generate interrupt */
281         writel(0, IIM_BASE_ADDR + IIM_STATM_OFF);
282         // clear the status bits and error bits
283         writel(0x3, IIM_BASE_ADDR + IIM_STAT_OFF);
284         writel(0xFE, IIM_BASE_ADDR + IIM_ERR_OFF);
285 }
286
287 /*
288  * The action should be either:
289  *                      POLL_FUSE_PRGD
290  * or:
291  *                      POLL_FUSE_SNSD
292  */
293 static int poll_fuse_op_done(int action)
294 {
295
296         u32 status, error;
297
298         if (action != POLL_FUSE_PRGD && action != POLL_FUSE_SNSD) {
299                 diag_printf("%s(%d) invalid operation\n", __FUNCTION__, action);
300                 return -1;
301         }
302
303         /* Poll busy bit till it is NOT set */
304         while ((readl(IIM_BASE_ADDR + IIM_STAT_OFF) & IIM_STAT_BUSY) != 0 ) {
305         }
306
307         /* Test for successful write */
308         status = readl(IIM_BASE_ADDR + IIM_STAT_OFF);
309         error = readl(IIM_BASE_ADDR + IIM_ERR_OFF);
310
311         if ((status & action) != 0 && (error & (action >> IIM_ERR_SHIFT)) == 0) {
312                 if (error) {
313                         diag_printf("Even though the operation seems successful...\n");
314                         diag_printf("There are some error(s) at addr=0x%02lx: 0x%02x\n",
315                                                 (IIM_BASE_ADDR + IIM_ERR_OFF), error);
316                 }
317                 return 0;
318         }
319         diag_printf("%s(%d) failed\n", __FUNCTION__, action);
320         diag_printf("status address=0x%02lx, value=0x%02x\n",
321                                 (IIM_BASE_ADDR + IIM_STAT_OFF), status);
322         diag_printf("There are some error(s) at addr=0x%02lx: 0x%02x\n",
323                                 (IIM_BASE_ADDR + IIM_ERR_OFF), error);
324         return -1;
325 }
326
327 static void sense_fuse(int bank, int row, int bit)
328 {
329         int ret;
330         int addr, addr_l, addr_h, reg_addr;
331
332         fuse_op_start();
333
334         addr = ((bank << 11) | (row << 3) | (bit & 0x7));
335         /* Set IIM Program Upper Address */
336         addr_h = (addr >> 8) & 0x000000FF;
337         /* Set IIM Program Lower Address */
338         addr_l = (addr & 0x000000FF);
339
340 #ifdef IIM_FUSE_DEBUG
341         diag_printf("%s: addr_h=0x%02x, addr_l=0x%02x\n",
342                                 __FUNCTION__, addr_h, addr_l);
343 #endif
344         writel(addr_h, IIM_BASE_ADDR + IIM_UA_OFF);
345         writel(addr_l, IIM_BASE_ADDR + IIM_LA_OFF);
346         /* Start sensing */
347         writel(0x8, IIM_BASE_ADDR + IIM_FCTL_OFF);
348         if ((ret = poll_fuse_op_done(POLL_FUSE_SNSD)) != 0) {
349                 diag_printf("%s(bank: %d, row: %d, bit: %d failed\n",
350                                         __FUNCTION__, bank, row, bit);
351         }
352         reg_addr = IIM_BASE_ADDR + IIM_SDAT_OFF;
353         if (ret == 0)
354                 diag_printf("fuses at (bank:%d, row:%d) = 0x%02x\n", bank, row, readl(reg_addr));
355 }
356
357 void do_fuse_read(int argc, char *argv[])
358 {
359         unsigned long bank, row;
360
361         if (argc == 1) {
362                 diag_printf("Useage: fuse_read <bank> <row>\n");
363                 return;
364         } else if (argc == 3) {
365                 if (!parse_num(argv[1], &bank, &argv[1], " ")) {
366                         diag_printf("Error: Invalid parameter\n");
367                         return;
368                 }
369                 if (!parse_num(argv[2], &row, &argv[2], " ")) {
370                         diag_printf("Error: Invalid parameter\n");
371                         return;
372                 }
373
374                 diag_printf("Read fuse at bank:%ld row:%ld\n", bank, row);
375                 sense_fuse(bank, row, 0);
376
377         } else {
378                 diag_printf("Passing in wrong arguments: %d\n", argc);
379                 diag_printf("Useage: fuse_read <bank> <row>\n");
380         }
381 }
382
383 /* Blow fuses based on the bank, row and bit positions (all 0-based)
384 */
385 int fuse_blow(int bank, int row, int bit)
386 {
387         int addr, addr_l, addr_h, ret = -1;
388
389         fuse_op_start();
390
391         /* Disable IIM Program Protect */
392         writel(0xAA, IIM_BASE_ADDR + IIM_PREG_P_OFF);
393
394         addr = ((bank << 11) | (row << 3) | (bit & 0x7));
395         /* Set IIM Program Upper Address */
396         addr_h = (addr >> 8) & 0x000000FF;
397         /* Set IIM Program Lower Address */
398         addr_l = (addr & 0x000000FF);
399
400 #ifdef IIM_FUSE_DEBUG
401         diag_printf("blowing fuse %d %d bit %d addr_h=0x%02x, addr_l=0x%02x\n",
402                                 bank, row, bit, addr_h, addr_l);
403 #endif
404
405         writel(addr_h, IIM_BASE_ADDR + IIM_UA_OFF);
406         writel(addr_l, IIM_BASE_ADDR + IIM_LA_OFF);
407         /* Start Programming */
408         writel(0x71, IIM_BASE_ADDR + IIM_FCTL_OFF);
409         if (poll_fuse_op_done(POLL_FUSE_PRGD) == 0) {
410                 ret = 0;
411         }
412
413         /* Enable IIM Program Protect */
414         writel(0x0, IIM_BASE_ADDR + IIM_PREG_P_OFF);
415         return ret;
416 }
417
418 /*
419  * This command is added for burning IIM fuses
420  */
421 RedBoot_cmd("fuse_read",
422                         "read some fuses",
423                         "<bank> <row>",
424                         do_fuse_read
425         );
426
427 RedBoot_cmd("fuse_blow",
428                         "blow some fuses",
429                         "<bank> <row> <value>",
430                         do_fuse_blow
431         );
432
433 #define                 INIT_STRING                             "12345678"
434 static char ready_to_blow[] = INIT_STRING;
435
436 void quick_itoa(u32 num, char *a)
437 {
438         int i, j, k;
439         for (i = 0; i <= 7; i++) {
440                 j = (num >> (4 * i)) & 0xF;
441                 k = (j < 10) ? '0' : ('a' - 0xa);
442                 a[i] = j + k;
443         }
444 }
445
446 void do_fuse_blow(int argc, char *argv[])
447 {
448         unsigned long bank, row, value;
449         int i;
450
451         if (argc == 1) {
452                 diag_printf("It is too dangeous for you to use this command.\n");
453                 return;
454         } else if (argc == 2) {
455                 if (strcasecmp(argv[1], "nandboot") == 0) {
456                         quick_itoa(readl(EPIT_BASE_ADDR + EPITCNR), ready_to_blow);
457                         diag_printf("%s\n", ready_to_blow);
458                 }
459                 return;
460         } else if (argc == 3) {
461                 if (strcasecmp(argv[1], "nandboot") == 0 &&
462                         strcasecmp(argv[2], ready_to_blow) == 0) {
463 #if defined(CYGPKG_HAL_ARM_MXC91131) || defined(CYGPKG_HAL_ARM_MX21) || \
464         defined(CYGPKG_HAL_ARM_MX27) || defined(CYGPKG_HAL_ARM_MX31) || \
465         defined(CYGPKG_HAL_ARM_MX35) || defined(CYGPKG_HAL_ARM_MX25)
466                         diag_printf("No need to blow any fuses for NAND boot on this platform\n\n");
467 #else
468 #error "Are you sure you want this?"
469                         diag_printf("Ready to burn NAND boot fuses\n");
470                         if (fuse_blow(0, 16, 1) != 0 || fuse_blow(0, 16, 7) != 0) {
471                                 diag_printf("NAND BOOT fuse blown failed miserably ...\n");
472                         } else {
473                                 diag_printf("NAND BOOT fuse blown successfully ...\n");
474                         }
475                 } else {
476                         diag_printf("Not ready: %s, %s\n", argv[1], argv[2]);
477 #endif
478                 }
479         } else if (argc == 4) {
480                 if (!parse_num(argv[1], &bank, &argv[1], " ")) {
481                                 diag_printf("Error: Invalid fuse bank\n");
482                                 return;
483                 }
484                 if (!parse_num(argv[2], &row, &argv[2], " ")) {
485                                 diag_printf("Error: Invalid fuse row\n");
486                                 return;
487                 }
488                 if (!parse_num(argv[3], &value, &argv[3], " ")) {
489                                 diag_printf("Error: Invalid value\n");
490                                 return;
491                 }
492                 if (!verify_action("Confirm to blow fuse at bank:%ld row:%ld value:0x%02lx (%ld)",
493                                                    bank, row, value)) {
494                         diag_printf("fuse_blow canceled\n");
495                         return;
496                 }
497
498                 diag_printf("Blowing fuse at bank:%ld row:%ld value:%ld\n",
499                                         bank, row, value);
500                 for (i = 0; i < 8; i++) {
501                         if (((value >> i) & 0x1) == 0) {
502                                 continue;
503                         }
504                         if (fuse_blow(bank, row, i) != 0) {
505                                 diag_printf("fuse_blow(bank: %ld, row: %ld, bit: %d failed\n",
506                                                         bank, row, i);
507                         } else {
508                                 diag_printf("fuse_blow(bank: %ld, row: %ld, bit: %d successful\n",
509                                                         bank, row, i);
510                         }
511                 }
512                 sense_fuse(bank, row, 0);
513         } else {
514                 diag_printf("Passing in wrong arguments: %d\n", argc);
515         }
516         /* Reset to default string */
517         strcpy(ready_to_blow, INIT_STRING);
518 }
519
520 /* precondition: m>0 and n>0.  Let g=gcd(m,n). */
521 int gcd(int m, int n)
522 {
523         int t;
524         while (m > 0) {
525                 if (n > m) {t = m; m = n; n = t;} /* swap */
526                 m -= n;
527         }
528         return n;
529 }