]> git.kernelconcepts.de Git - karo-tx-redboot.git/blob - packages/hal/arm/mx21/var/v2_0/src/cmds.c
Initial revision
[karo-tx-redboot.git] / packages / hal / arm / mx21 / var / v2_0 / src / cmds.c
1 //==========================================================================
2 //
3 //      cmds.c
4 //
5 //      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 typedef unsigned long long  u64;
48 typedef unsigned int        u32;
49 typedef unsigned short      u16;
50 typedef unsigned char       u8;
51
52 #define SZ_DEC_1M       1000000
53 #define PLL_PD_MAX      16      //actual pd+1
54 #define PLL_MFI_MAX     15
55 #define PLL_MFI_MIN     5
56 #define PLL_MFD_MAX     1024    //actual mfd+1
57 #define PLL_MFN_MAX     1022
58 #define PRESC_MAX       8
59 #define IPG_DIV_MAX     2
60 #define AHB_DIV_MAX     16
61
62 //#define CPLM_SETUP      (1 << 31)
63 #define CPLM_SETUP      0
64
65 #if (PLL_REF_CLK == FREQ_32768HZ) || (PLL_REF_CLK == FREQ_32000HZ)
66 #define PLL_MFD_FIXED   1024
67 #endif
68 #if (PLL_REF_CLK == FREQ_26MHZ)
69 #define PLL_MFD_FIXED   (26 * 16)       // =416
70 #endif
71
72 #define PLL_FREQ_MAX    (2 * PLL_REF_CLK * PLL_MFI_MAX)
73 #define PLL_FREQ_MIN    ((2 * PLL_REF_CLK * PLL_MFI_MIN) / PLL_PD_MAX)
74 #define AHB_CLK_MAX     133333333
75 #define IPG_CLK_MAX     (AHB_CLK_MAX / 2)
76 #define NFC_CLK_MAX     33333333
77
78 #define ERR_WRONG_CLK   -1
79 #define ERR_NO_MFI      -2
80 #define ERR_NO_MFN      -3
81 #define ERR_NO_PD       -4
82 #define ERR_NO_PRESC    -5
83
84 u32 pll_clock(enum plls pll);
85 u32 get_main_clock(enum main_clocks clk);
86 u32 get_peri_clock(enum peri_clocks clk);
87
88 static void clock_setup(int argc, char *argv[]);
89 static void clko(int argc, char *argv[]);
90
91 RedBoot_cmd("clock",
92             "Setup/Display clock (max AHB=133MHz, max IPG=66.5MHz)\nSyntax:",
93             "[<core clock in MHz> [:<AHB-to-core divider>[:<IPG-to-AHB divider>]]] \n\n\
94 If a divider is zero or no divider is specified, the optimal divider values \n\
95 will be chosen. Examples:\n\
96    [clock]         -> Show various clocks\n\
97    [clock 266]     -> Core=266  AHB=133           IPG=66.5\n\
98    [clock 350]     -> Core=350  AHB=117           IPG=58.5\n\
99    [clock 266:4]   -> Core=266  AHB=66.5(Core/4)  IPG=66.5\n\
100    [clock 266:4:2] -> Core=266  AHB=66.5(Core/4)  IPG=33.25(AHB/2)\n",
101             clock_setup
102            );
103
104 /*!
105  * This is to calculate various parameters based on reference clock and 
106  * targeted clock based on the equation:
107  *      t_clk = 2*ref_freq*(mfi + mfn/(mfd+1))/(pd+1)
108  * This calculation is based on a fixed MFD value for simplicity.
109  *
110  * @param ref       reference clock freq
111  * @param target    targeted clock in HZ
112  * @param p_pd      calculated pd value (pd value from register + 1) upon return
113  * @param p_mfi     calculated actual mfi value upon return
114  * @param p_mfn     calculated actual mfn value upon return
115  * @param p_mfd     fixed mfd value (mfd value from register + 1) upon return
116  *
117  * @return          0 if successful; non-zero otherwise.
118  */
119 int calc_pll_params(u32 ref, u32 target, u32 *p_pd, 
120                     u32 *p_mfi, u32 *p_mfn, u32 *p_mfd)
121 {
122     u64 pd, mfi, mfn, n_target = (u64)target, n_ref = (u64)ref;
123
124     // Make sure targeted freq is in the valid range. Otherwise the 
125     // following calculation might be wrong!!!
126     if (target < PLL_FREQ_MIN || target > PLL_FREQ_MAX) {
127         return ERR_WRONG_CLK;
128     }
129     // Use n_target and n_ref to avoid overflow
130     for (pd = 1; pd <= PLL_PD_MAX; pd++) {
131         mfi = (n_target * pd) / (2 * n_ref);
132         if (mfi > PLL_MFI_MAX) {
133             return ERR_NO_MFI;
134         } else if (mfi < 5) {
135             continue;
136         }
137         break;
138     }
139     // Now got pd and mfi already
140     mfn = (((n_target * pd) / 2 - n_ref * mfi) * PLL_MFD_FIXED) / n_ref;
141     // Check mfn within limit and mfn < denominator
142     if (mfn > PLL_MFN_MAX || mfn >= PLL_MFD_FIXED) {
143         return ERR_NO_MFN;
144     }
145
146     if (pd > PLL_PD_MAX) {
147         return ERR_NO_PD;
148     }
149     *p_pd = (u32)pd;
150     *p_mfi = (u32)mfi;
151     *p_mfn = (u32)mfn;
152     *p_mfd = PLL_MFD_FIXED;
153     return 0;
154 }
155
156 /*!
157  * This function assumes the expected core clock has to be changed by
158  * modifying the PLL. This is NOT true always but for most of the times,
159  * it is. So it assumes the PLL output freq is the same as the expected 
160  * core clock (presc=1) unless the core clock is less than PLL_FREQ_MIN.
161  * In the latter case, it will try to increase the presc value until 
162  * (presc*core_clk) is greater than PLL_FREQ_MIN. It then makes call to
163  * calc_pll_params() and obtains the values of PD, MFI,MFN, MFD based
164  * on the targeted PLL and reference input clock to the PLL. Lastly, 
165  * it sets the register based on these values along with the dividers.
166  * Note 1) There is no value checking for the passed-in divider values
167  *         so the caller has to make sure those values are sensible.
168  *      2) Also adjust the NFC divider such that the NFC clock doesn't
169  *         exceed NFC_CLK_MAX (which is 33MHz now).
170  * 
171  * @param ref       pll input reference clock (32KHz or 26MHz)
172  * @param core_clk  core clock in Hz
173  * @param ahb_div   ahb divider to divide the core clock to get ahb clock 
174  *                  (ahb_div - 1) needs to be set in the register
175  * @param ipg_div   ipg divider to divide the ahb clock to get ipg clock
176  *                  (ipg_div - 1) needs to be set in the register
177  # @return          0 if successful; non-zero otherwise
178  */
179 int configure_clock(u32 ref, u32 core_clk, u32 ahb_div, u32 ipg_div)
180 {
181     u32 pll, presc = 1, pd, mfi, mfn, mfd, brmo = 1, cscr, mpctl0;
182     u32 pcdr0, nfc_div;
183     int ret, i;
184
185     // assume pll default to core clock first
186     pll = core_clk;
187     // when core_clk >= PLL_FREQ_MIN, the presc can be 1.
188     // Otherwise, need to calculate presc value below and adjust the targeted pll
189     if (core_clk < PLL_FREQ_MIN) {
190         for (presc = 1; presc <= PRESC_MAX; presc++) {
191             if ((core_clk * presc) > PLL_FREQ_MIN) {
192                 break;
193             }
194         }
195         if (presc == (PRESC_MAX + 1)) {
196             diag_printf("can't make presc=%d\n", presc);
197             return ERR_NO_PRESC;
198         }
199         pll = core_clk * presc;
200     }
201     // pll is now the targeted pll output. Use it along with ref input clock
202     // to get pd, mfi, mfn, mfd
203     if ((ret = calc_pll_params(ref, pll, &pd, &mfi, &mfn, &mfd)) != 0) {
204         diag_printf("can't find pll parameters: %d\n", ret);
205         return ret;
206     }
207
208     // blindly increase divider first to avoid too fast ahbclk and ipgclk
209     // in case the core clock increases too much
210     cscr = readl(MX21_CRM_BASE + MX21_CRM_CSCR);
211     cscr &= ~0xE0003E00;
212     // increase the dividers. should work even when core clock is 780MHz
213     // which is unlikely true.
214     cscr |= (3 << 29) | (5 << 10) | (1 << 9);
215     writel(cscr, MX21_CRM_BASE + MX21_CRM_CSCR);
216
217     // update PLL register
218     if ((mfd < (10 * mfn)) && ((10 * mfn) < (9 * mfd)))
219         brmo = 0;
220     if (brmo != 0)
221         writel(1 << 6, MX21_CRM_BASE + MX21_CRM_MPCTL1);
222
223     mpctl0 = readl(MX21_CRM_BASE + MX21_CRM_MPCTL0);
224     mpctl0 = (mpctl0 & 0xC000C000)  |
225              CPLM_SETUP             |
226              ((pd - 1) << 26)       | 
227              ((mfd - 1) << 16)      |
228              (mfi << 10)            | 
229              mfn;
230     writel(mpctl0, MX21_CRM_BASE + MX21_CRM_MPCTL0);
231     // restart mpll
232     writel((cscr | (1 << 21)), MX21_CRM_BASE + MX21_CRM_CSCR);
233     // check the LF bit to insure lock
234     while ((readl(MX21_CRM_BASE + MX21_CRM_MPCTL1) & (1 << 15)) == 0);
235     // have to add some delay for new values to take effect
236     for (i = 0; i < 10000; i++);
237
238     // PLL locked already so use the new divider values
239     cscr = readl(MX21_CRM_BASE + MX21_CRM_CSCR);
240     cscr &= ~0xE0003E00;
241     cscr |= ((presc - 1) << 29) | ((ahb_div - 1) << 10) | ((ipg_div - 1) << 9);
242     writel(cscr, MX21_CRM_BASE + MX21_CRM_CSCR);
243
244     // Make sure optimal NFC clock but less than NFC_CLK_MAX
245     for (nfc_div = 1; nfc_div <= 16; nfc_div++) {
246         if ((core_clk / nfc_div) <= NFC_CLK_MAX) {
247             break;
248         }
249     }
250     pcdr0 = readl(MX21_CRM_BASE + MX21_CRM_PCDR0);
251     writel(((pcdr0 & 0xFFFF0FFF) | ((nfc_div - 1) << 12)), 
252            MX21_CRM_BASE + MX21_CRM_PCDR0);
253
254     return 0;
255 }
256
257 static void clock_setup(int argc,char *argv[])
258 {
259     u32 i, core_clk, ipg_div, data[3],
260     ahb_div, ahb_clk, ipg_clk;
261     int ret;
262
263     if (argc == 1)
264         goto print_clock;
265     for (i = 0;  i < 3;  i++) {
266         unsigned long temp;
267         if (!parse_num(*(&argv[1]), &temp, &argv[1], ":")) {
268             diag_printf("Error: Invalid parameter\n");
269             return;
270         }
271         data[i] = temp;
272     }
273
274     core_clk = data[0] * SZ_DEC_1M;
275     ahb_div = data[1];  // actual register field + 1
276     ipg_div = data[2];  // actual register field + 1
277
278     if (core_clk < (PLL_FREQ_MIN / PRESC_MAX) || core_clk > PLL_FREQ_MAX) {
279         diag_printf("Targeted core clock should be within [%d - %d]\n", 
280                     PLL_FREQ_MIN / PRESC_MAX, PLL_FREQ_MAX);
281         return;
282     }
283
284     // find the ahb divider  
285     if (ahb_div > AHB_DIV_MAX) {
286         diag_printf("Invalid AHB divider: %d. Maximum value is %d\n",
287                     ahb_div, AHB_DIV_MAX);
288         return;
289     }
290     if (ahb_div == 0) {
291         // no AHBCLK divider specified
292         for (ahb_div = 1; ; ahb_div++) {
293             if ((core_clk / ahb_div) <= AHB_CLK_MAX) {
294                 break;
295             }
296         }
297     }
298     if (ahb_div > AHB_DIV_MAX || (core_clk / ahb_div) > AHB_CLK_MAX) {
299         diag_printf("Can't make AHB=%d since max=%d\n", 
300                     core_clk / ahb_div, AHB_CLK_MAX);
301         return;
302     }
303
304     // find the ipg divider
305     ahb_clk = core_clk / ahb_div;
306     if (ipg_div > IPG_DIV_MAX) {
307         diag_printf("Invalid IPG divider: %d. Maximum value is %d\n", 
308                     ipg_div, IPG_DIV_MAX);
309         return;
310     }
311     if (ipg_div == 0) {
312         ipg_div++;          // At least =1
313         if (ahb_clk > IPG_CLK_MAX)
314             ipg_div++;      // Make it =2
315     }
316     if (ipg_div > IPG_DIV_MAX || (ahb_clk / ipg_div) > IPG_CLK_MAX) {
317         diag_printf("Can't make IPG=%d since max=%d\n", 
318                     (ahb_clk / ipg_div), IPG_CLK_MAX);
319         return;
320     }
321     ipg_clk = ahb_clk / ipg_div;
322
323     diag_printf("Trying to set core=%d ahb=%d ipg=%d...\n", 
324                 core_clk, ahb_clk, ipg_clk);
325     
326     // stop the serial to be ready to adjust the clock
327     hal_delay_us(100000);
328     cyg_hal_plf_serial_stop();
329     // adjust the clock
330     ret = configure_clock(PLL_REF_CLK, core_clk, ahb_div, ipg_div);
331     // restart the serial driver
332     cyg_hal_plf_serial_init();
333     hal_delay_us(100000);
334
335     if (ret != 0) {
336         diag_printf("Failed to setup clock: %d\n", ret);
337         return;
338     }
339     diag_printf("\n<<<New clock setting>>>\n");
340
341     // Now printing clocks
342 print_clock:
343     diag_printf("\nMPLL\t\tSPLL\n");
344     diag_printf("=========================\n");
345     diag_printf("%-16d%-16d\n\n", pll_clock(MCU_PLL), pll_clock(SER_PLL));
346     diag_printf("CPU\t\tAHB\t\tIPG\t\tNFC\t\tUSB\n");
347     diag_printf("========================================================================\n");
348     diag_printf("%-16d%-16d%-16d%-16d%-16d\n\n",
349                 get_main_clock(CPU_CLK),
350                 get_main_clock(AHB_CLK),
351                 get_main_clock(IPG_CLK),
352                 get_main_clock(NFC_CLK),
353                 get_main_clock(USB_CLK));
354
355     diag_printf("PER1\t\tPER2\t\tPER3\t\tPER4\n");
356     diag_printf("===========================================");
357     diag_printf("=============\n");
358
359     diag_printf("%-16d%-16d%-16d%-16d\n\n", 
360                 get_peri_clock(PER_CLK1),
361                 get_peri_clock(PER_CLK2),
362                 get_peri_clock(PER_CLK3),
363                 get_peri_clock(PER_CLK4));
364
365     diag_printf("FIRI\t\tSSI1\t\tSSI2\n");
366     diag_printf("========================================\n");
367     diag_printf("%-16d%-16d%-16d\n\n", 
368                 get_peri_clock(FIRI_BAUD),
369                 get_peri_clock(SSI1_BAUD),
370                 get_peri_clock(SSI2_BAUD));
371     diag_printf("PERCLK: 1-<UART|GPT|PWM> 2-<SDHC|CSPI> 3-<LCDC> 4-<CSI>\n");
372 }
373
374 /*!
375  * This function returns the PLL output value in Hz based on pll.
376  */
377 u32 pll_clock(enum plls pll)
378 {
379     u64 mfi, mfn, mfd, pdf, ref_clk, pll_out;
380     u64 reg = readl(pll);
381
382     pdf = (reg >> 26) & 0xF;
383     mfd = (reg >> 16) & 0x3FF;
384     mfi = (reg >> 10) & 0xF;
385     mfi = (mfi <= 5) ? 5: mfi;
386     mfn = reg & 0x3FF;
387
388     ref_clk = PLL_REF_CLK;
389
390     pll_out = (2 * ref_clk * mfi + ((2 * ref_clk * mfn) / (mfd + 1))) /
391               (pdf + 1);
392
393     return (u32)pll_out;
394 }
395
396 /*!
397  * This function returns the main clock value in Hz.
398  */
399 u32 get_main_clock(enum main_clocks clk)
400 {
401     u32 presc, ahb_div, ipg_pdf, nfc_div;
402     u32 ret_val = 0, usb_div;
403     u32 cscr = readl(MX21_CRM_BASE + MX21_CRM_CSCR);
404     u32 pcdr0 = readl(MX21_CRM_BASE + MX21_CRM_PCDR0);
405
406     presc = (cscr >> 29) + 1;
407
408     switch (clk) {
409     case CPU_CLK:
410         ret_val = pll_clock(MCU_PLL) / presc;
411         break;
412     case AHB_CLK:
413         ahb_div = ((cscr >> 10) & 0xF) + 1;
414         ret_val = pll_clock(MCU_PLL) / (presc * ahb_div);
415         break;
416     case IPG_CLK:
417         ahb_div = ((cscr >> 10) & 0xF) + 1;
418         ipg_pdf = ((cscr >> 9) & 0x1) + 1;
419         ret_val = pll_clock(MCU_PLL) / (presc * ahb_div * ipg_pdf);
420         break;
421     case NFC_CLK:
422         nfc_div = ((pcdr0 >> 12) & 0xF) + 1;
423         /* AHB/nfc_div */
424         ret_val = pll_clock(MCU_PLL) / (presc * nfc_div);
425         break;
426     case USB_CLK:
427         usb_div = ((cscr >> 26) & 0x7) + 1;
428         ret_val = pll_clock(SER_PLL) / usb_div;
429         break;
430     default:
431         diag_printf("Unknown clock: %d\n", clk);
432         break;
433     }
434
435     return ret_val;
436 }
437
438 /*!
439  * This function returns the peripheral clock value in Hz.
440  */
441 u32 get_peri_clock(enum peri_clocks clk)
442 {
443     u32 ret_val = 0, div;
444     u32 pcdr0 = readl(MX21_CRM_BASE + MX21_CRM_PCDR0);
445     u32 pcdr1 = readl(MX21_CRM_BASE + MX21_CRM_PCDR1);
446     u32 cscr = readl(MX21_CRM_BASE + MX21_CRM_CSCR);
447
448     switch (clk) {
449     case PER_CLK1:
450         div = (pcdr1 & 0x3F) + 1;
451         ret_val = pll_clock(MCU_PLL) / div;
452         break;
453     case PER_CLK2:
454         div = ((pcdr1 >> 8) & 0x3F) + 1;
455         ret_val = pll_clock(MCU_PLL) / div;
456         break;
457     case PER_CLK3:
458         div = ((pcdr1 >> 16) & 0x3F) + 1;
459         ret_val = pll_clock(MCU_PLL) / div;
460         break;
461     case PER_CLK4:
462         div = ((pcdr1 >> 24) & 0x3F) + 1;
463         ret_val = pll_clock(MCU_PLL) / div;
464         break;
465     case SSI1_BAUD:
466         div = (pcdr0 >> 16) & 0x3F;
467         if (div < 2)
468             div = 62 * 2;
469         if ((cscr & (1 << 19)) != 0) {
470             // This takes care of 0.5*SSIDIV[0] by x2
471             ret_val = (2 * pll_clock(MCU_PLL)) / div;
472         } else {
473             ret_val = (2 * pll_clock(SER_PLL)) / div;
474         }
475         break;
476     case SSI2_BAUD:
477         div = (pcdr0 >> 26) & 0x3F;
478         if (div < 2)
479             div = 62 * 2;
480         if ((cscr & (1 << 20)) != 0) {
481             ret_val = (2 * pll_clock(MCU_PLL)) / div;
482         } else {
483             ret_val = (2 * pll_clock(SER_PLL)) / div;
484         }
485         break;
486     case FIRI_BAUD:
487         div = (pcdr0 & 0x1F) + 1;
488         if ((cscr & (1 << 18)) == 0) {
489             ret_val = pll_clock(MCU_PLL) / div;
490         } else {
491             ret_val = (2 * pll_clock(SER_PLL)) / div;
492         }
493         break;
494     default:
495         diag_printf("%s(): This clock: %d not supported yet \n",
496                     __FUNCTION__, clk);
497         break;
498     }
499
500     return ret_val;
501 }
502
503 RedBoot_cmd("clko",
504             "Select clock source for CLKO (TP1 on the CPU daughter card)",
505             " The output clock is the actual clock source freq. Default is FCLK\n\
506          Note that the module clock will be turned on for reading!\n\
507           <0> - display current clko selection \n\
508           <1> - CLK32 \n\
509           <2> - PREMCLK \n\
510           <3> - CLK26M (may see nothing if 26MHz Crystal is not connected) \n\
511           <4> - MPLL Reference CLK \n\
512           <5> - SPLL Reference CLK \n\
513           <6> - MPLL CLK \n\
514           <7> - SPLL CLK \n\
515           <8> - FCLK \n\
516           <9> - AHBCLK \n\
517           <10> - IPG_CLK (PERCLK) \n\
518           <11> - PERCLK1 \n\
519           <12> - PERCLK2 \n\
520           <13> - PERCLK3 \n\
521           <14> - PERCLK4 \n\
522           <15> - SSI 1 Baud \n\
523           <16> - SSI 2 Baud \n\
524           <17> - NFC \n\
525           <18> - FIRI Baud \n\
526           <19> - CLK48M Always \n\
527           <20> - CLK32K Always \n\
528           <21> - CLK48M \n\
529           <22> - CLK48DIV_CLKO",
530             clko
531            );
532
533 static u8* clko_name[] ={
534     "NULL",
535     "CLK32",
536     "PREMCLK",
537     "CLK26M (may see nothing if 26MHz Crystal is not connected)",
538     "MPLL Reference CLK",
539     "SPLL Reference CLK",
540     "MPLL CLK",
541     "SPLL CLK",
542     "FCLK",
543     "AHBCLK",
544     "IPG_CLK (PERCLK)",
545     "PERCLK1",
546     "PERCLK2",
547     "PERCLK3",
548     "PERCLK4",
549     "SSI 1 Baud",
550     "SSI 2 Baud",
551     "NFC",
552     "FIRI Baud",
553     "CLK48M Always",
554     "CLK32K Always",
555     "CLK48M",
556     "CLK48DIV_CLKO",
557 };
558
559 // This has to agree with the above table
560 enum clko_src {
561     PERCLK3     = 13,
562     PERCLK4     = 14,
563     SSI_1_Baud  = 15,
564     SSI_2_Baud  = 16,
565     NFC_Baud    = 17,
566     FIRI_Baud   = 18,
567 };
568
569 #define CLKO_MAX_INDEX          (sizeof(clko_name) / sizeof(u8*))
570
571 static void clko(int argc,char *argv[])
572 {
573     u32 action = 0, ccsr;
574
575     if (!scan_opts(argc, argv, 1, 0, 0, (void*) &action,
576                    OPTION_ARG_TYPE_NUM, "action"))
577         return;
578
579     if (action >= CLKO_MAX_INDEX) {
580         diag_printf("%d is not supported\n\n", action);
581         return;
582     }
583
584     ccsr = readl(MX21_CRM_BASE + MX21_CRM_CCSR);
585
586     if (action != 0) {
587         // turn on these clocks
588         switch (action) {
589         case PERCLK3:
590             writel(readl(MX21_CRM_BASE + MX21_CRM_PCCR0) | (1 << 18),
591                    MX21_CRM_BASE + MX21_CRM_PCCR0);
592             break;
593         case PERCLK4:
594             writel(readl(MX21_CRM_BASE + MX21_CRM_PCCR0) | (1 << 22),
595                    MX21_CRM_BASE + MX21_CRM_PCCR0);
596             break;
597         case SSI_1_Baud:
598             writel(readl(MX21_CRM_BASE + MX21_CRM_PCCR0) | (1 << 17),
599                    MX21_CRM_BASE + MX21_CRM_PCCR0);
600             break;
601         case SSI_2_Baud:
602             writel(readl(MX21_CRM_BASE + MX21_CRM_PCCR0) | (1 << 16),
603                    MX21_CRM_BASE + MX21_CRM_PCCR0);
604             break;
605         case NFC_Baud:
606             writel(readl(MX21_CRM_BASE + MX21_CRM_PCCR0) | (1 << 19),
607                    MX21_CRM_BASE + MX21_CRM_PCCR0);
608             break;
609         case FIRI_Baud:
610             writel(readl(MX21_CRM_BASE + MX21_CRM_PCCR0) | (1 << 20),
611                    MX21_CRM_BASE + MX21_CRM_PCCR0);
612             break;
613         }
614         ccsr = (ccsr & (~0x1F)) + action - 1;
615         writel(ccsr, MX21_CRM_BASE + MX21_CRM_CCSR);
616         diag_printf("Set clko to ");
617     }
618
619     ccsr = readl(MX21_CRM_BASE + MX21_CRM_CCSR);
620     diag_printf("%s\n", clko_name[(ccsr & 0x1F) + 1]);
621     diag_printf("CCSR register[0x%x] = 0x%x\n", 
622                 (MX21_CRM_BASE + MX21_CRM_CCSR), ccsr);
623 }
624
625 extern int flash_program(void *_addr, void *_data, int len, void **err_addr);
626 extern int flash_erase(void *addr, int len, void **err_addr);
627
628 void auto_flash_start(void)
629 {
630     void *err_addr;
631         int stat;
632     int nor_update = 1; //todo: need to support NAND
633     u32 src = readl(SERIAL_DOWNLOAD_SRC_REG);
634     u32 dst = readl(SERIAL_DOWNLOAD_TGT_REG);
635     u32 sz = readl(SERIAL_DOWNLOAD_SZ_REG);
636
637     if (readl(SERIAL_DOWNLOAD_MAGIC_REG) != SERIAL_DOWNLOAD_MAGIC) {
638         return;
639     }
640
641     if (nor_update) {
642         // Erase area to be programmed
643         if ((stat = flash_erase((void *)dst, sz, (void **)&err_addr)) != 0) {
644             diag_printf("BEADDEAD\n");
645         return;
646         }
647         diag_printf("BEADBEEF\n");
648         // Now program it
649         if ((stat = flash_program((void *)dst, (void *)src, sz, 
650                                   (void **)&err_addr)) != 0) {
651             diag_printf("BEADFEEF\n");
652         }
653     }
654     diag_printf("BEADCEEF\n");
655 }
656
657 RedBoot_init(auto_flash_start, RedBoot_INIT_LAST);