]> git.kernelconcepts.de Git - karo-tx-redboot.git/blob - packages/hal/arm/mxc91231/var/v2_0/src/cmds.c
Initial revision
[karo-tx-redboot.git] / packages / hal / arm / mxc91231 / 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 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 ARM_DIV_MAX     6       //should be enough even though max is 12
57 #define IPG_DIV_MAX     16
58 #define AHB_DIV_MAX     16
59 #define NFC_DIV_MAX     16
60
61 #define REF_IN_CLK_NUM  4
62 struct fixed_pll_mfd {
63     u32 ref_clk_hz;
64     u32 mfd;
65 };
66 const struct fixed_pll_mfd fixed_mfd[REF_IN_CLK_NUM] = {
67     {FREQ_CKIH_26M,       26 * 16},    // 416
68     {0,                   0},      // reserved
69     {2 * FREQ_CKIH_26M,   26 * 16},    // 416
70     {0,                   0},      // reserved
71 };
72
73 struct pll_param {
74     u32 pd;
75     u32 mfi;
76     u32 mfn;
77     u32 mfd;
78 };
79
80 #define PLL_FREQ_MAX(_ref_clk_)    (2 * _ref_clk_ * PLL_MFI_MAX)
81 #define PLL_FREQ_MIN(_ref_clk_)    ((2 * _ref_clk_ * (PLL_MFI_MIN - 1)) / PLL_PD_MAX)
82 #define AHB_CLK_MAX     133333333
83 #define IPG_CLK_MAX     (AHB_CLK_MAX / 2)
84 #define NFC_CLK_MAX     25000000
85
86 #define ERR_WRONG_CLK   -1
87 #define ERR_NO_MFI      -2
88 #define ERR_NO_MFN      -3
89 #define ERR_NO_PD       -4
90 #define ERR_NO_ARM_DIV  -5
91 #define ERR_NO_AHB_DIV  -6
92
93 int gcd(int m, int n);
94
95 static void clock_setup(int argc, char *argv[]);
96 static void ckol(int argc, char *argv[]);
97 static void ckoh(int argc, char *argv[]);
98
99 static volatile u32 *crm_ap_base = REG32_PTR(CRM_AP_BASE_ADDR);
100
101 static volatile u32 *pll_base[] =
102 {
103     REG32_PTR(PLL0_BASE_ADDR),     // MCU PLL
104     REG32_PTR(PLL1_BASE_ADDR),     // DSP PLL
105     REG32_PTR(PLL2_BASE_ADDR),     // USB PLL
106 };
107
108 #define NOT_ON_VAL  0xDEADBEEF
109
110 RedBoot_cmd("clock",
111             "Setup/Display clock (max AHB=133MHz, max IPG=66.5MHz)\nSyntax:",
112             "[<core clock in MHz> [:<AHB-to-core divider>[:<IPG-to-AHB divider>]]] \n\n\
113 If a divider is zero or no divider is specified, the optimal divider values \n\
114 will be chosen. Examples:\n\
115    [clock]         -> Show various clocks\n\
116    [clock 532]     -> Core=532  AHB=133           IPG=66.5\n\
117    [clock 399]     -> Core=399  AHB=133           IPG=66.5\n\
118    [clock 399:6]   -> Core=399  AHB=66.5(Core/8)  IPG=66.5\n\
119    [clock 399:6:2] -> Core=532  AHB=66.5(Core/8)  IPG=33.25(AHB/2)\n",
120             clock_setup
121            );
122
123 /*!
124  * This is to calculate various parameters based on reference clock and 
125  * targeted clock based on the equation:
126  *      t_clk = 2*ref_freq*(mfi + mfn/(mfd+1))/(pd+1)
127  * This calculation is based on a fixed MFD value for simplicity.
128  *
129  * @param ref       reference clock freq in Hz
130  * @param target    targeted clock in Hz
131  * @param p_pd      calculated pd value (pd value from register + 1) upon return
132  * @param p_mfi     calculated actual mfi value upon return
133  * @param p_mfn     calculated actual mfn value upon return
134  * @param p_mfd     fixed mfd value (mfd value from register + 1) upon return
135  *
136  * @return          0 if successful; non-zero otherwise.
137  */
138 int calc_pll_params(u32 ref, u32 target, struct pll_param *pll)
139 {
140     u64 pd, mfi = 1, mfn, mfd, n_target = target, n_ref = ref, i;
141
142     // make sure targeted freq is in the valid range. Otherwise the 
143     // following calculation might be wrong!!!
144     if (n_target < PLL_FREQ_MIN(ref) || n_target > PLL_FREQ_MAX(ref))
145         return ERR_WRONG_CLK;
146     for (i = 0; ; i++) {
147         if (i == REF_IN_CLK_NUM)
148             return ERR_WRONG_CLK;
149         if (fixed_mfd[i].ref_clk_hz == ref) {
150             mfd = fixed_mfd[i].mfd;
151             break;
152         }
153     }
154     // use n_target and n_ref to avoid overflow
155     for (pd = 1; pd <= PLL_PD_MAX; pd++) {
156         mfi = (n_target * pd) / (2 * n_ref);
157         if (mfi > PLL_MFI_MAX)
158             return ERR_NO_MFI;
159         else if (mfi < 5)
160             continue;
161         break;
162     }
163     // Now got pd and mfi already
164     mfn = (((n_target * pd) / 2 - n_ref * mfi) * mfd) / n_ref;
165 #ifdef CMD_CLOCK_DEBUG
166     diag_printf("%d: ref=%d, target=%d, pd=%d, mfi=%d,mfn=%d, mfd=%d\n", 
167                 __LINE__, ref, (u32)n_target, (u32)pd, (u32)mfi, (u32)mfn, (u32)mfd);
168 #endif    
169     i = 1;
170     if (mfn != 0)
171         i = gcd(mfd, mfn);
172     pll->pd = (u32)pd;
173     pll->mfi = (u32)mfi;
174     pll->mfn = (u32)(mfn / i);
175     pll->mfd = (u32)(mfd / i);
176     return 0;
177 }
178
179 /*!
180  * This function assumes the expected core clock has to be changed by
181  * modifying the PLL. This is NOT true always but for most of the times,
182  * it is. So it assumes the PLL output freq is the same as the expected 
183  * core clock (arm_div=1) unless the core clock is less than PLL_FREQ_MIN.
184  * In the latter case, it will try to increase the arm_div value until 
185  * (arm_div*core_clk) is greater than PLL_FREQ_MIN. It then makes call to
186  * calc_pll_params() and obtains the values of PD, MFI,MFN, MFD based
187  * on the targeted PLL and reference input clock to the PLL. Lastly, 
188  * it sets the register based on these values along with the dividers.
189  * Note 1) There is no value checking for the passed-in divider values
190  *         so the caller has to make sure those values are sensible.
191  *      2) Also adjust the NFC divider such that the NFC clock doesn't
192  *         exceed NFC_CLK_MAX.
193  *      3) This function should not have allowed diag_printf() calls since
194  *         the serial driver has been stoped. But leave then here to allow
195  *         easy debugging by NOT calling the cyg_hal_plf_serial_stop().
196  *      4) The IPG divider doesn't go through AHB divider
197  * 
198  * @param ref       pll input reference clock (32KHz or 26MHz)
199  * @param core_clk  core clock in Hz
200  * @param ahb_div   ahb divider to divide the core clock to get ahb clock 
201  *                  (ahb_div - 1) needs to be set in the register
202  * @param ipg_div   ipg divider to divide the core clock to get ipg clock
203  *                  (ipg_div - 1) needs to be set in the register
204  # @return          0 if successful; non-zero otherwise
205  */
206 int configure_clock(u32 ref, u32 core_clk, u32 ahb_div, u32 ipg_div)
207 {
208     u32 pll, arm_div = 1, nfc_div, acdr, acder2;
209     struct pll_param pll_param;
210     int ret;
211
212     // assume pll default to core clock first
213     pll = core_clk;
214     // when core_clk >= PLL_FREQ_MIN, the arm_div can be 1.
215     // Otherwise, need to calculate arm_div value below and adjust the targeted pll
216     if (core_clk < PLL_FREQ_MIN(ref)) {
217         for (arm_div = 1; arm_div <= ARM_DIV_MAX; arm_div++) {
218             if ((core_clk * arm_div) > PLL_FREQ_MIN(ref)) {
219                 break;
220             }
221         }
222         if (arm_div == (ARM_DIV_MAX + 1)) {
223             diag_printf("can't make arm_div=%d\n", arm_div);
224             return ERR_NO_ARM_DIV;
225         }
226         pll = core_clk * arm_div;
227     }
228
229     // get nfc_div - make sure optimal NFC clock but less than NFC_CLK_MAX
230     for (nfc_div = 1; nfc_div <= NFC_DIV_MAX; nfc_div++) {
231         if ((pll / (ahb_div * nfc_div)) <= NFC_CLK_MAX) {
232             break;
233         }
234     }
235
236     // pll is now the targeted pll output. Use it along with ref input clock
237     // to get pd, mfi, mfn, mfd
238     if ((ret = calc_pll_params(ref, pll, &pll_param)) != 0) {
239         diag_printf("can't find pll parameters: %d\n", ret);
240         return ret;
241     }
242 #ifdef CMD_CLOCK_DEBUG
243     diag_printf("ref=%d, pll=%d, pd=%d, mfi=%d,mfn=%d, mfd=%d\n", 
244                 ref, pll, pll_param.pd, pll_param.mfi, pll_param.mfn, pll_param.mfd);
245 #endif
246     acdr = (((arm_div == 1) ? 0x8 : (arm_div - 2)) << 8) |
247            (((ahb_div == 16)? 0x0 : ahb_div) << 4) |
248            ((ipg_div == 16)? 0x0 : ipg_div);
249
250     acder2 = (readl(CRM_AP_BASE_ADDR + CRM_AP_ACDER2) & 0xFFF0FFFF) |
251              ((nfc_div - 1) << 16);
252
253     // switch to ap_ref_clk
254     writel(readl(CRM_AP_BASE_ADDR + CRM_AP_ACSR) & (~0x1), 
255            CRM_AP_BASE_ADDR + CRM_AP_ACSR);
256
257     // change the dividers
258     writel(acdr, CRM_AP_BASE_ADDR + CRM_AP_ACDR);
259     writel(acder2, CRM_AP_BASE_ADDR + CRM_AP_ACDER2);
260
261     // adjust pll settings
262     writel(((pll_param.pd - 1) << 0) | (pll_param.mfi << 4), 
263            PLL0_BASE_ADDR + PLL_DP_OP);
264     writel(pll_param.mfn, PLL0_BASE_ADDR + PLL_DP_MFN);
265     writel(pll_param.mfd - 1, PLL0_BASE_ADDR + PLL_DP_MFD);
266     writel(((pll_param.pd - 1) << 0) | (pll_param.mfi << 4), 
267            PLL0_BASE_ADDR + PLL_DP_HFS_OP);
268     writel(pll_param.mfn, PLL0_BASE_ADDR + PLL_DP_HFS_MFN);
269     writel(pll_param.mfd - 1, PLL0_BASE_ADDR + PLL_DP_HFS_MFD);
270
271     // switch back to pll
272     writel(readl(CRM_AP_BASE_ADDR + CRM_AP_ACSR) | 0x1, 
273            CRM_AP_BASE_ADDR + CRM_AP_ACSR);
274
275     return 0;
276 }
277
278 static void clock_setup(int argc,char *argv[])
279 {
280     u32 i, core_clk, ipg_div, data[3], uart1_baud, uart3_baud, ssi1_baud;
281     u32 ssi2_baud, csi_baud, ahb_div, ahb_clk, ipg_clk, clk_sel, ref_clk;
282     int ret;
283
284     if (argc == 1)
285         goto print_clock;
286     for (i = 0;  i < 3;  i++) {
287         unsigned long temp;
288         if (!parse_num(*(&argv[1]), &temp, &argv[1], ":")) {
289             diag_printf("Error: Invalid parameter\n");
290             return;
291         }
292         data[i] = temp;
293     }
294
295     core_clk = data[0] * SZ_DEC_1M;
296     ahb_div = data[1];  // actual register field + 1
297     ipg_div = data[2];  // actual register field + 1
298
299     // since only support set clock for the AP domain, get ref input clock
300     // for the AP domain.
301     clk_sel = MXC_GET_FIELD(readl(PLL0_BASE_ADDR + PLL_DP_CTL), 2, 8);
302     ref_clk = fixed_mfd[clk_sel].ref_clk_hz;
303
304     if (core_clk < (PLL_FREQ_MIN(ref_clk) / ARM_DIV_MAX) || 
305         core_clk > PLL_FREQ_MAX(ref_clk)) {
306         diag_printf("Targeted core clock should be within [%d - %d]\n", 
307                     PLL_FREQ_MIN(ref_clk) / ARM_DIV_MAX, 
308                     PLL_FREQ_MAX(ref_clk));
309         return;
310     }
311
312     // find the ahb divider  
313     if (ahb_div > AHB_DIV_MAX) {
314         diag_printf("Invalid AHB divider: %d. Maximum value is %d\n",
315                     ahb_div, AHB_DIV_MAX);
316         return;
317     }
318     if (ahb_div == 0) {
319         // no HCLK divider specified
320         for (ahb_div = 1; ; ahb_div++) {
321             if ((core_clk / ahb_div) <= AHB_CLK_MAX)
322                 break;
323         }
324     }
325     if (ahb_div > AHB_DIV_MAX || (core_clk / ahb_div) > AHB_CLK_MAX) {
326         diag_printf("Can't make AHB=%d since max=%d\n", 
327                     core_clk / ahb_div, AHB_CLK_MAX);
328         return;
329     }
330
331     // find the ipg divider
332     ahb_clk = core_clk / ahb_div;
333     if (ipg_div == 0) {
334         ipg_div++;          // At least =1
335         if (ahb_clk > IPG_CLK_MAX)
336             ipg_div++;      // Make it =2
337     }
338     // adjust ipg_div since IPG clock doesn't go through AHB divider
339     ipg_div *= ahb_div;
340     ipg_clk = core_clk / ipg_div;
341     if (ipg_div > IPG_DIV_MAX || ipg_clk > IPG_CLK_MAX) {
342         if (ipg_div > IPG_DIV_MAX)
343             diag_printf("Invalid IPG divider: %d. Max is: %d\n", 
344                         ipg_div / ahb_div, IPG_DIV_MAX / ahb_div);
345         else
346             diag_printf("Can't make IPG=%dHz since max=%dHz\n", 
347                         ipg_clk, IPG_CLK_MAX);
348         return;
349     }
350
351     diag_printf("Trying to set core=%d ahb=%d ipg=%d...\n", 
352                 core_clk, ahb_clk, ipg_clk);
353
354     // stop the serial to be ready to adjust the clock
355     hal_delay_us(100000);
356     cyg_hal_plf_serial_stop();
357     // adjust the clock
358     ret = configure_clock(ref_clk, core_clk, ahb_div, ipg_div);
359     // restart the serial driver
360     cyg_hal_plf_serial_init();
361     hal_delay_us(100000);
362
363     if (ret != 0) {
364         diag_printf("Failed to setup clock: %d\n", ret);
365         return;
366     }
367     diag_printf("\n<<<New clock setting>>>\n");
368
369     // Now printing clocks
370 print_clock:
371     diag_printf("\nMCUPLL\t\tUSBPLL\t\tDSPPLL\n");
372     diag_printf("========================================\n");
373     diag_printf("%-16d%-16d%-16d\n\n", pll_clock(PLL0), pll_clock(PLL2),
374                 pll_clock(PLL1));
375     diag_printf("CPU\t\tAHB\t\tIPG\t\tNFC\t\tUSB\n");
376     diag_printf("===========================================");
377     diag_printf("=============================\n");
378     diag_printf("%-16d%-16d%-16d%-16d%-16d\n\n",
379                 get_main_clock(CPU_CLK),
380                 get_main_clock(AHB_CLK),
381                 get_main_clock(IPG_CLK),
382                 get_main_clock(NFC_CLK),
383                 get_main_clock(USB_CLK));
384
385     uart1_baud = get_peri_clock(UART1_BAUD);
386     uart3_baud = get_peri_clock(UART3_BAUD);
387     ssi1_baud = get_peri_clock(SSI1_BAUD);
388     ssi2_baud = get_peri_clock(SSI2_BAUD);
389     csi_baud = get_peri_clock(CSI_BAUD);
390
391     diag_printf("UART1/2\t\tUART3\t\tSSI1\t\tSSI2\t\tCSI\n");
392     diag_printf("===========================================");
393     diag_printf("=============================\n");
394
395     (uart1_baud != NOT_ON_VAL) ? diag_printf("%-16d", uart1_baud) :
396                                  diag_printf("%-16s", "OFF");
397     (uart3_baud != NOT_ON_VAL) ? diag_printf("%-16d", uart3_baud) :
398                                  diag_printf("%-16s", "OFF");
399     (ssi1_baud != NOT_ON_VAL) ? diag_printf("%-16d", ssi1_baud) :
400                                 diag_printf("%-16s", "OFF");
401     (ssi2_baud != NOT_ON_VAL) ? diag_printf("%-16d", ssi2_baud) :
402                                 diag_printf("%-16s", "OFF");
403     (csi_baud != NOT_ON_VAL) ? diag_printf("%-16d", csi_baud ) :
404                                diag_printf("%-16s", "OFF");
405     diag_printf("\n\n");
406 }
407
408 /*!
409  * This function returns the PLL output value in Hz based on pll.
410  */
411 u32 pll_clock(enum plls pll)
412 {
413     u64 mfi, mfn, mfd, pdf, ref_clk, pll_out, sign;
414     u64 dp_op, dp_mfd, dp_mfn, clk_sel;
415
416     clk_sel = MXC_GET_FIELD(pll_base[pll][PLL_DP_CTL >> 2], 2, 8);
417     ref_clk = fixed_mfd[clk_sel].ref_clk_hz;
418
419     if ((pll_base[pll][PLL_DP_CTL >> 2] & 0x80) == 0) {
420         dp_op = pll_base[pll][PLL_DP_OP >> 2];
421         dp_mfd = pll_base[pll][PLL_DP_MFD >> 2];
422         dp_mfn = pll_base[pll][PLL_DP_MFN >> 2];
423     } else {
424         dp_op = pll_base[pll][PLL_DP_HFS_OP >> 2];
425         dp_mfd = pll_base[pll][PLL_DP_HFS_MFD >> 2];
426         dp_mfn = pll_base[pll][PLL_DP_HFS_MFN >> 2];
427     }
428     pdf = dp_op & 0xF;
429     mfi = (dp_op >> 4) & 0xF;
430     mfi = (mfi <= 5) ? 5: mfi;
431     mfd = dp_mfd & 0x07FFFFFF;
432     mfn = dp_mfn & 0x07FFFFFF;
433
434     sign = (mfn < 0x4000000) ? 0: 1;
435     mfn = (mfn <= 0x4000000) ? mfn: (0x8000000 - mfn);
436
437     if (sign == 0) {
438         pll_out = (2 * ref_clk * mfi + ((2 * ref_clk * mfn) / (mfd + 1))) /
439                   (pdf + 1);
440     } else {
441         pll_out = (2 * ref_clk * mfi - ((2 * ref_clk * mfn) / (mfd + 1))) /
442                   (pdf + 1);
443     }
444
445     return (u32)pll_out;
446 }
447
448 const u32 CRM_SMALL_DIV[] = {2, 3, 4, 5, 6, 8, 10, 12};
449
450 /*!
451  * This function returns the main clock dividers.
452  */
453 u32 clock_divider(enum main_clocks clk)
454 {
455     u32 div = 0;
456     u32 acdr, acder2;
457
458     acdr = crm_ap_base[CRM_AP_ACDR >> 2];
459     acder2 = crm_ap_base[CRM_AP_ACDER2 >> 2];
460
461     switch (clk) {
462     case CPU_CLK:
463         div = (acdr >> 8) & 0xF;
464         div = (div > 7) ? 1 : (CRM_SMALL_DIV[div]);
465         break;
466     case AHB_CLK:
467         div = (acdr >> 4) & 0xF;
468         div = (div == 0) ? 16 : div;
469         break;
470     case IPG_CLK:
471         div = (acdr >> 0) & 0xF;
472         div = (div == 0) ? 16 : div;
473         break;
474     case NFC_CLK:
475         div = ((acder2 >> 16) & 0xF) + 1;
476         break;
477     case USB_CLK:
478         div = (acder2 >> 8) & 0xF;
479         div = (div > 7) ? 1 : (CRM_SMALL_DIV[div]);
480         break;
481     default:
482         diag_printf("Wrong clock: %d\n", clk);
483         break;
484     }
485
486     return div;
487 }
488
489 /*!
490  * This function returns the peripheral clock dividers.
491  */
492 u32 clock_peri_divider(enum peri_clocks clk)
493 {
494     u32 div = 0;
495     u32 apra, acder1, acder2;
496
497     apra = crm_ap_base[CRM_AP_APRA >> 2];
498     acder1 = crm_ap_base[CRM_AP_ACDER1 >> 2];
499     acder2 = crm_ap_base[CRM_AP_ACDER2 >> 2];
500
501     switch (clk) {
502     case UART1_BAUD:
503     case UART2_BAUD:
504         div = acder2 & 0xF;
505         div = (div > 7) ? 1 : (CRM_SMALL_DIV[div]);
506         break;
507     case UART3_BAUD:
508         div = (apra >> 17) & 0xF;
509         div = (div > 7) ? 1 : (CRM_SMALL_DIV[div]);
510         break;
511     case SSI1_BAUD:
512         div = acder1 & 0x3F;
513         //double the divider to avoid FP
514         div = (div == 0 || div == 1) ? (2 * 62) : div;
515         break;
516     case SSI2_BAUD:
517         div = (acder1 >> 8) & 0x3F;
518         //double the divider to avoid FP
519         div = (div == 0 || div == 1) ? 62 : div;
520         break;
521     case CSI_BAUD:
522         div = (acder1 >> 24) & 0x3F;
523         //double the divider to avoid FP
524         div = (div == 0 || div == 1) ? 62 : div;
525         break;
526     default:
527         diag_printf("Wrong clock: %d\n", clk);
528         break;
529     }
530
531     return div;
532 }
533
534 void get_ref_clk(u32 *ap_unc_pat_ref, u32 *ap_ref_x2,
535                  u32 *ap_ref)
536 {
537     u32 ap_pat_ref_div_1, ap_pat_ref_div_2, ap_isel,
538         ascsr, adcr, acder2, clk_sel, ref_clk;
539
540     clk_sel = MXC_GET_FIELD(readl(PLL0_BASE_ADDR + PLL_DP_CTL), 2, 8);
541     ref_clk = fixed_mfd[clk_sel].ref_clk_hz;
542
543     ascsr = crm_ap_base[CRM_AP_ASCSR >> 2];
544     adcr = crm_ap_base[CRM_AP_ADCR >> 2];
545     acder2 = crm_ap_base[CRM_AP_ACDER2 >> 2];
546
547     ap_isel = ascsr & 0x1;
548     ap_pat_ref_div_1 = ((ascsr >> 2) & 0x1) + 1;
549     ap_pat_ref_div_2 = ((ascsr >> 15) & 0x1) + 1;
550
551     *ap_unc_pat_ref = ref_clk * (ap_isel + 1);
552     *ap_ref_x2 =  (*ap_unc_pat_ref)/ ap_pat_ref_div_1;
553     *ap_ref = (*ap_ref_x2) / ap_pat_ref_div_2;
554 }
555
556 /*!
557  * This function returns the main clock value in Hz.
558  */
559 u32 get_main_clock(enum main_clocks clk)
560 {
561     u32 ret_val = 0, apsel, ap_clk_pre_dfs, acsr, ascsr, adcr, acder2;
562     u32 lfdf = 1, ap_ref_x2_clk, ap_ref_clk, usbsel, ap_unc_pat_ref;
563
564     acsr = crm_ap_base[CRM_AP_ACSR >> 2];
565     ascsr = crm_ap_base[CRM_AP_ASCSR >> 2];
566     adcr = crm_ap_base[CRM_AP_ADCR >> 2];
567     acder2 = crm_ap_base[CRM_AP_ACDER2 >> 2];
568
569     get_ref_clk(&ap_unc_pat_ref, &ap_ref_x2_clk, &ap_ref_clk);
570
571     if ((acsr & 0x1) == 0) {
572         // inverted pat_ref is selected
573         ap_clk_pre_dfs = ap_ref_clk;
574     } else {
575         // Now AP domain runs off the pll
576         apsel = (ascsr >> 3) & 0x3;
577         ap_clk_pre_dfs = pll_clock(apsel) / clock_divider(CPU_CLK);
578     }
579
580     switch (clk) {
581     case CPU_CLK:
582         if (((adcr & 0x2) == 0) && ((adcr & 0x20) != 0) && ((adcr & 0x80) == 0)) {
583             // DFS divider used
584             lfdf = 2 ^ ((adcr >> 8) & 0x3);
585         }
586         ret_val = ap_clk_pre_dfs / lfdf;
587         break;
588     case AHB_CLK:
589         ret_val = ap_clk_pre_dfs / clock_divider(AHB_CLK);
590         break;
591     case IPG_CLK:
592         ret_val = ap_clk_pre_dfs / clock_divider(IPG_CLK);
593         break;
594     case NFC_CLK:
595         if ((acder2 & (1 << 20)) == 0) {
596             diag_printf("Warning: NFC clock is not enabled !!!\n");
597         } else {
598             ret_val = ap_clk_pre_dfs / (clock_divider(AHB_CLK) *
599                                         clock_divider(NFC_CLK));
600         }
601         break;
602     case USB_CLK:
603         if ((acder2 & (1 << 12)) == 0) {
604             diag_printf("Warning: USB clock is not enabled !!!\n");
605         } else {
606             if ((acsr & 0x1) == 0) {
607                 // inverted pat_ref is selected
608                 ret_val = ap_ref_clk / clock_divider(USB_CLK);
609             } else {
610                 usbsel = (ascsr >> 13) & 0x3;
611                 if (usbsel == 0x3)
612                     diag_printf("reserved source for USB input\n");
613                 else
614                     ret_val = pll_clock(usbsel) / clock_divider(USB_CLK);
615             }
616         }
617         break;
618     default:
619         break;
620     }
621
622     return ret_val;
623 }
624
625 /*!
626  * This function returns the peripheral clock value in Hz.
627  */
628 u32 get_peri_clock(enum peri_clocks clk)
629 {
630     u32 apra, ascsr, acder1, acder2, ap_unc_pat_ref,
631     ap_ref_x2_clk, ap_ref_clk, ret_val = 0, sel;
632
633     apra = crm_ap_base[CRM_AP_APRA >> 2];
634     acder1 = crm_ap_base[CRM_AP_ACDER1 >> 2];
635     acder2 = crm_ap_base[CRM_AP_ACDER2 >> 2];
636     ascsr = crm_ap_base[CRM_AP_ASCSR >> 2];
637
638     get_ref_clk(&ap_unc_pat_ref, &ap_ref_x2_clk, &ap_ref_clk);
639
640     switch (clk) {
641     case UART1_BAUD:
642         if ((apra & 0x1) == 0) {
643             return NOT_ON_VAL;
644         }
645         ret_val = ap_unc_pat_ref / clock_peri_divider(UART1_BAUD);
646         break;
647     case UART2_BAUD:
648         if ((apra & 0x100) == 0) {
649             return NOT_ON_VAL;
650         }
651         ret_val = ap_unc_pat_ref / clock_peri_divider(UART2_BAUD);
652         break;
653     case UART3_BAUD:
654         if ((apra & 0x10000) == 0) {
655             return NOT_ON_VAL;
656         }
657         ret_val = ap_unc_pat_ref / clock_peri_divider(UART3_BAUD);
658         break;
659     case SSI1_BAUD:
660         if ((acder1 & (1 << 6)) == 0) {
661             return NOT_ON_VAL;
662         }
663
664         sel = (ascsr >> 5) & 0x3;
665         // Don't forget to double the divider
666         ret_val = (2 * pll_clock(sel)) / clock_peri_divider(SSI1_BAUD);
667         break;
668     case SSI2_BAUD:
669         if ((acder1 & (1 << 14)) == 0) {
670             return NOT_ON_VAL;
671         }
672
673         sel = (ascsr >> 7) & 0x3;
674         // Don't forget to double the divider
675         ret_val = (2 *pll_clock(sel)) / clock_peri_divider(SSI2_BAUD);
676         break;
677     case CSI_BAUD:
678         if ((acder1 & (1 << 30)) == 0) {
679             return NOT_ON_VAL;
680         }
681
682         sel = (ascsr >> 11) & 0x3;
683         // Don't forget to double the divider
684         ret_val = (2 * pll_clock(sel)) / (clock_peri_divider(CSI_BAUD));
685         break;
686     }
687
688     return ret_val;
689 }
690
691 RedBoot_cmd("ckoh",
692             "Select clock source for CKOH (J9 on CPU daughter card)",
693             " Default is 1/10 of ARM core\n\
694           <0> - display current ckoh selection \n\
695           <1> - ap_uncorrected_pat_ref_clk \n\
696           <2> - ungated_ap_clk (ARM Core in normal case) \n\
697           <3> - ungated_ap_ahb_clk (AHB) \n\
698           <4> - ungated_ap_pclk (IPG) \n\
699           <5> - usb_clk \n\
700           <6> - ap_perclk (baud clock) \n\
701           <7> - ap_ckil_clk (sync) \n\
702           <8> - ap_pat_ref_clk (ungated sync) \n\
703           <<The following only valid for Rev2.0 silicon and above>> \n\
704           <9> - crm_ap_nfc_clk \n\
705           <10> - ap_async_pat_ref_clk for EL1T and MQSPI \n\
706           <11> - ap_sdhc1_perclk \n\
707           <12> - ap_ahb_div2_clk (for SAHARA) \n\
708           <13> - ipu_lpmc_hsp_clk\n",
709             ckoh
710            );
711
712 static u8* div_str[] = {
713     "1/2 of ",
714     "1/3 of ",
715     "1/4 of ",
716     "1/5 of ",
717     "1/6 of ",
718     "1/8 of ",
719     "1/10 of ",
720     "1/12 of ",
721     "",
722     "",
723     "",
724     "",
725     "",
726     "",
727     "",
728     "",
729 };
730
731 static u8* ckoh_name[] ={
732     "NULL",
733     "ap_uncorrected_pat_ref_clk",
734     "ungated_ap_clk (ARM Core in normal case)",
735     "ungated_ap_ahb_clk (AHB)",
736     "ungated_ap_pclk (IPG)",
737     "usb_clk",
738     "ap_perclk (baud clock)",
739     "ap_ckil_clk (sync)",
740     "ap_pat_ref_clk (ungated sync)",
741     "crm_ap_nfc_clk",
742     "ap_async_pat_ref_clk for EL1T and MQSPI",
743     "ap_sdhc1_perclk",
744     "ap_ahb_div2_clk (for SAHARA)",
745     "ipu_lpmc_hsp_clk",
746 };
747
748 #define CKOH_MAX_INDEX          (sizeof(ckoh_name) / sizeof(u8*))
749 #define CKOH_DIV                6  // default divide by 10
750
751 extern u32 system_rev;
752
753 static void ckoh(int argc,char *argv[])
754 {
755     u32 action = 0, val, new_val, div = 0x8, i;
756
757     if (!scan_opts(argc, argv, 1, 0, 0, (void*) &action,
758                    OPTION_ARG_TYPE_NUM, "action"))
759         return;
760
761     if (action >= CKOH_MAX_INDEX || 
762         (((system_rev >> 4) & 0xF) == CHIP_REV_1_0 && action > 8)) {
763         diag_printf("%d is not supported\n\n", action);
764         return;
765     }
766
767     val = readl(CRM_AP_BASE_ADDR + CRM_AP_ACR);
768
769     if (action != 0) {
770         // set CKOHDIV to be 6 for dividing by 10
771         if (action == 2 || action == 3)
772             div = CKOH_DIV;
773         action--;
774         // clear CKOHS-HIGH, CKOHD, CHOHS, CKOHDIV bits and
775         new_val = (val & (~(1 << 18 | 0xFF00))) | (div << 8);
776         if (action > 7) {
777             new_val |= (1 << 18) | ((action & 7) << 12); 
778         } else {
779             new_val |= action << 12;
780         }
781         writel(new_val, CRM_AP_BASE_ADDR + CRM_AP_ACR);
782         diag_printf("\nSet ckoh to ");
783     }
784
785     val = readl(CRM_AP_BASE_ADDR + CRM_AP_ACR);
786     /* locate the index in the name table */
787     new_val = ((val >> 15) & 8) | ((val >> 12) & 7);
788     i = (val >> 8) & 0xF;
789     diag_printf("%s%s\n", div_str[i], ckoh_name[new_val + 1]);
790     diag_printf("ACR register[0x%x]=0x%x\n\n", 
791                 (CRM_AP_BASE_ADDR + CRM_AP_ACR), val);
792 }
793
794 RedBoot_cmd("ckol",
795             "Select clock source for CKO (J10 on EVB CPU card)",
796             " Default is CKIL\n\
797           <0> - display current cko selection\n\
798           <1> - ckil \n\
799           <2> - ap_pat_ref_clk (ungated sync) \n\
800           <3> - ap_ref_x2_clk \n\
801           <4} - ssi1_clk \n\
802           <5> - ssi2_clk \n\
803           <6> - cs_clk \n\
804           <7> - RESERVED \n\
805           <8> - RESERVED \n\
806           <<The following only valid for Rev2.0 silicon and above>> \n\
807           <9> - dfm_ckil_multiply_clk \n\
808           <10> - ap_sdhc2_perclk \n\
809           <11> - ap_uart3_per_clk \n",
810             ckol
811            );
812
813 static u8* cko_name[] ={
814     "NULL",
815     "ckil",
816     "ap_pat_ref_clk (ungated sync)",
817     "ap_ref_x2_clk",
818     "ssi1_clk",
819     "ssi2_clk",
820     "cs_clk",
821     "RESERVED",
822     "RESERVED",
823     "dfm_ckil_multiply_clk",
824     "ap_sdhc2_perclk",
825     "ap_uart3_per_clk",
826 };
827
828 #define CKO_MAX_INDEX           (sizeof(cko_name) / sizeof(u8*))
829
830 static void ckol(int argc,char *argv[])
831 {
832     u32 action = 0, val, new_val, t;
833
834     if (!scan_opts(argc, argv, 1, 0, 0, (void*) &action,
835                    OPTION_ARG_TYPE_NUM, "action"))
836         return;
837
838     if (action >= CKO_MAX_INDEX || 
839         (((system_rev >> 4) & 0xF) == CHIP_REV_1_0 && action > 6) ||
840         (action >= 7 && action <= 8)) {
841         diag_printf("%d is not supported\n\n", action);
842         return;
843     }
844
845     val = readl(CRM_AP_BASE_ADDR + CRM_AP_ACR);
846
847     if (action != 0) {
848         // turn on these clocks
849         switch (action) {
850         case 4: //SSI1
851             t = readl(CRM_AP_BASE_ADDR + CRM_AP_ACDER1);
852             writel(t | (1 << 6), CRM_AP_BASE_ADDR + CRM_AP_ACDER1);
853             break;
854         case 5: //SSI2
855             t = readl(CRM_AP_BASE_ADDR + CRM_AP_ACDER1);
856             writel(t | (1 << 14), CRM_AP_BASE_ADDR + CRM_AP_ACDER1);
857             break;
858         case 6: //cs_clk
859             t = readl(CRM_AP_BASE_ADDR + CRM_AP_ACDER1);
860             writel(t | (1 << 30), CRM_AP_BASE_ADDR + CRM_AP_ACDER1);
861             break;
862
863         }
864         action--;
865         /* clear CKOS-HIGH, CKOD, CHOS bits and */
866         new_val = val & (~((1 << 16) | 0xF0));
867         if (action > 5) {
868             new_val |= (1 << 16) | ((action & 7) << 4); 
869         } else {
870             new_val |= action << 4;
871         }
872         writel(new_val, CRM_AP_BASE_ADDR + CRM_AP_ACR);
873         diag_printf("\nSet cko to ");
874     }
875
876     val = readl(CRM_AP_BASE_ADDR + CRM_AP_ACR);
877     /* locate the index in the name table */
878     new_val = ((val >> 13) & 8) | ((val >> 4) & 7);
879
880     diag_printf("%s\nACR register[0x%x]=0x%x\n\n", cko_name[new_val + 1],
881                 (CRM_AP_BASE_ADDR + CRM_AP_ACR), val);
882 }
883
884 #ifdef L2CC_ENABLED
885 /*
886  * This command is added for some simple testing only. It turns on/off
887  * L2 cache regardless of L1 cache state. The side effect of this is
888  * when doing any flash operations such as "fis init", the L2
889  * will be turned back on along with L1 caches even though it is off
890  * by using this command.
891  */
892 RedBoot_cmd("L2",
893             "L2 cache",
894             "[ON | OFF]",
895             do_L2_caches
896            );
897
898 void do_L2_caches(int argc, char *argv[])
899 {
900     u32 oldints;
901     int L2cache_on=0;
902
903     if (argc == 2) {
904         if (strcasecmp(argv[1], "on") == 0) {
905             HAL_DISABLE_INTERRUPTS(oldints);
906             HAL_ENABLE_L2();
907             HAL_RESTORE_INTERRUPTS(oldints);
908         } else if (strcasecmp(argv[1], "off") == 0) {
909             HAL_DISABLE_INTERRUPTS(oldints);
910             HAL_CLEAN_INVALIDATE_L2();
911             HAL_DISABLE_L2();
912             HAL_RESTORE_INTERRUPTS(oldints);
913         } else {
914             diag_printf("Invalid L2 cache mode: %s\n", argv[1]);
915         }
916     } else {
917         HAL_L2CACHE_IS_ENABLED(L2cache_on);
918         diag_printf("L2 cache: %s\n", L2cache_on?"On":"Off");
919     }
920 }
921 #endif //L2CC_ENABLED
922
923 #define IIM_ERR_SHIFT       8
924 #define POLL_FUSE_PRGD      (IIM_STAT_PRGD | (IIM_ERR_PRGE << IIM_ERR_SHIFT))
925 #define POLL_FUSE_SNSD      (IIM_STAT_SNSD | (IIM_ERR_SNSE << IIM_ERR_SHIFT))
926
927 static void fuse_op_start(void)
928 {
929     /* Do not generate interrupt */
930     writel(0, IIM_BASE_ADDR + IIM_STATM_OFF);
931     // clear the status bits and error bits
932     writel(0x3, IIM_BASE_ADDR + IIM_STAT_OFF);
933     writel(0xFE, IIM_BASE_ADDR + IIM_ERR_OFF);
934 }
935
936 /*
937  * The action should be either:
938  *          POLL_FUSE_PRGD 
939  * or:
940  *          POLL_FUSE_SNSD
941  */
942 static int poll_fuse_op_done(int action)
943 {
944
945     u32 status, error;
946
947     if (action != POLL_FUSE_PRGD && action != POLL_FUSE_SNSD) {
948         diag_printf("%s(%d) invalid operation\n", __FUNCTION__, action);
949         return -1;
950     }
951
952     /* Poll busy bit till it is NOT set */
953     while ((readl(IIM_BASE_ADDR + IIM_STAT_OFF) & IIM_STAT_BUSY) != 0 ) {
954     }
955
956     /* Test for successful write */
957     status = readl(IIM_BASE_ADDR + IIM_STAT_OFF);
958     error = readl(IIM_BASE_ADDR + IIM_ERR_OFF);
959
960     if ((status & action) != 0 && (error & (action >> IIM_ERR_SHIFT)) == 0) {
961         if (error) {
962             diag_printf("Even though the operation seems successful...\n");
963             diag_printf("There are some error(s) at addr=0x%x: 0x%x\n",
964                         (IIM_BASE_ADDR + IIM_ERR_OFF), error);
965         }
966             return 0;
967         }
968     diag_printf("%s(%d) failed\n", __FUNCTION__, action);
969     diag_printf("status address=0x%x, value=0x%x\n",
970                 (IIM_BASE_ADDR + IIM_STAT_OFF), status);
971     diag_printf("There are some error(s) at addr=0x%x: 0x%x\n",
972                 (IIM_BASE_ADDR + IIM_ERR_OFF), error);
973     return -1;
974 }
975
976 static void sense_fuse(int bank, int row, int bit)
977 {
978     int addr, addr_l, addr_h, reg_addr;
979
980     fuse_op_start();
981     
982     addr = ((bank << 11) | (row << 3) | (bit & 0x7));
983     /* Set IIM Program Upper Address */
984     addr_h = (addr >> 8) & 0x000000FF;
985     /* Set IIM Program Lower Address */
986     addr_l = (addr & 0x000000FF);
987
988 #ifdef IIM_FUSE_DEBUG
989     diag_printf("%s: addr_h=0x%x, addr_l=0x%x\n",
990                 __FUNCTION__, addr_h, addr_l);
991 #endif
992     writel(addr_h, IIM_BASE_ADDR + IIM_UA_OFF);
993     writel(addr_l, IIM_BASE_ADDR + IIM_LA_OFF);
994     /* Start sensing */
995     writel(0x8, IIM_BASE_ADDR + IIM_FCTL_OFF);
996     if (poll_fuse_op_done(POLL_FUSE_SNSD) != 0) {
997         diag_printf("%s(bank: %d, row: %d, bit: %d failed\n",
998                     __FUNCTION__, bank, row, bit);
999     }
1000     reg_addr = IIM_BASE_ADDR + IIM_SDAT_OFF;
1001     diag_printf("fuses at (bank:%d, row:%d) = 0x%x\n", bank, row, readl(reg_addr));
1002 }
1003
1004 void do_fuse_read(int argc, char *argv[])
1005 {
1006     int bank, row;
1007
1008     if (argc == 1) {
1009         diag_printf("Useage: fuse_read <bank> <row>\n");
1010         return;
1011     } else if (argc == 3) {
1012         if (!parse_num(*(&argv[1]), (unsigned long *)&bank, &argv[1], " ")) {
1013                 diag_printf("Error: Invalid parameter\n");
1014             return;
1015         }
1016         if (!parse_num(*(&argv[2]), (unsigned long *)&row, &argv[2], " ")) {
1017                 diag_printf("Error: Invalid parameter\n");
1018                 return;
1019             }
1020
1021         diag_printf("Read fuse at bank:%d row:%d\n", bank, row);
1022         sense_fuse(bank, row, 0);
1023
1024     } else {
1025         diag_printf("Passing in wrong arguments: %d\n", argc);
1026         diag_printf("Useage: fuse_read <bank> <row>\n");
1027     }
1028 }
1029
1030 /* Blow fuses based on the bank, row and bit positions (all 0-based)
1031 */
1032 static int fuse_blow(int bank,int row,int bit)
1033 {
1034     int addr, addr_l, addr_h, ret = -1;
1035
1036     fuse_op_start();
1037
1038     /* Disable IIM Program Protect */
1039     writel(0xAA, IIM_BASE_ADDR + IIM_PREG_P_OFF);
1040
1041     addr = ((bank << 11) | (row << 3) | (bit & 0x7));
1042     /* Set IIM Program Upper Address */
1043     addr_h = (addr >> 8) & 0x000000FF;
1044     /* Set IIM Program Lower Address */
1045     addr_l = (addr & 0x000000FF);
1046
1047 #ifdef IIM_FUSE_DEBUG
1048     diag_printf("blowing addr_h=0x%x, addr_l=0x%x\n", addr_h, addr_l);
1049 #endif
1050
1051     writel(addr_h, IIM_BASE_ADDR + IIM_UA_OFF);
1052     writel(addr_l, IIM_BASE_ADDR + IIM_LA_OFF);
1053     /* Start Programming */
1054     writel(0x31, IIM_BASE_ADDR + IIM_FCTL_OFF);
1055     if (poll_fuse_op_done(POLL_FUSE_PRGD) == 0) {
1056         ret = 0;
1057     }
1058
1059     /* Enable IIM Program Protect */
1060     writel(0x0, IIM_BASE_ADDR + IIM_PREG_P_OFF);
1061     return ret;
1062 }
1063
1064 /*
1065  * This command is added for burning IIM fuses
1066  */
1067 RedBoot_cmd("fuse_read",
1068             "read some fuses",
1069             "<bank> <row>",
1070             do_fuse_read
1071            );
1072
1073 RedBoot_cmd("fuse_blow",
1074             "blow some fuses",
1075             "<bank> <row> <value>",
1076             do_fuse_blow
1077            );
1078
1079 #define         INIT_STRING              "12345678"
1080 static char ready_to_blow[] = INIT_STRING;
1081
1082 void quick_itoa(u32 num, char *a) 
1083 {
1084     int i, j, k;        
1085     for (i = 0; i <= 7; i++) {
1086         j = (num >> (4 * i)) & 0xF;
1087         k = (j < 10) ? '0' : ('a' - 0xa);
1088         a[i] = j + k;
1089     }
1090 }
1091
1092 void do_fuse_blow(int argc, char *argv[])
1093 {
1094     int bank, row, value, i;
1095
1096     if (argc == 1) {
1097         diag_printf("It is too dangeous for you to use this command.\n");
1098         return;
1099     } else if (argc == 2) {
1100         if (strcasecmp(argv[1], "nandboot") == 0) {
1101             quick_itoa(readl(EPIT_BASE_ADDR + EPITCNR), ready_to_blow);
1102             diag_printf("%s\n", ready_to_blow);
1103         }
1104         return;
1105     } else if (argc == 3) {
1106         if (strcasecmp(argv[1], "nandboot") == 0 && 
1107             strcasecmp(argv[2], ready_to_blow) == 0) {
1108 #if defined(CYGPKG_HAL_ARM_MXC91131) || defined(CYGPKG_HAL_ARM_MX21) || defined(CYGPKG_HAL_ARM_MX27) || defined(CYGPKG_HAL_ARM_MX31)
1109             diag_printf("No need to blow any fuses for NAND boot on this platform\n\n");
1110 #else
1111             diag_printf("Ready to burn NAND boot fuses\n");
1112             if (fuse_blow(0, 16, 1) != 0 || fuse_blow(0, 16, 7) != 0) {
1113                 diag_printf("NAND BOOT fuse blown failed miserably ...\n");
1114             } else {
1115                 diag_printf("NAND BOOT fuse blown successfully ...\n");
1116             }
1117         } else {
1118             diag_printf("Not ready: %s, %s\n", argv[1], argv[2]);
1119 #endif
1120         }
1121     } else if (argc == 4) {
1122         if (!parse_num(*(&argv[1]), (unsigned long *)&bank, &argv[1], " ")) {
1123                 diag_printf("Error: Invalid parameter\n");
1124             return;
1125         }
1126         if (!parse_num(*(&argv[2]), (unsigned long *)&row, &argv[2], " ")) {
1127                 diag_printf("Error: Invalid parameter\n");
1128                 return;
1129             }
1130         if (!parse_num(*(&argv[3]), (unsigned long *)&value, &argv[3], " ")) {
1131                 diag_printf("Error: Invalid parameter\n");
1132                 return;
1133             }
1134
1135         diag_printf("Blowing fuse at bank:%d row:%d value:%d\n",
1136                     bank, row, value);
1137         for (i = 0; i < 8; i++) {
1138             if (((value >> i) & 0x1) == 0) {
1139                 continue;
1140             }
1141             if (fuse_blow(bank, row, i) != 0) {
1142                 diag_printf("fuse_blow(bank: %d, row: %d, bit: %d failed\n",
1143                             bank, row, i);
1144             } else {
1145                 diag_printf("fuse_blow(bank: %d, row: %d, bit: %d successful\n",
1146                             bank, row, i);
1147             }
1148         }
1149         sense_fuse(bank, row, 0);
1150
1151     } else {
1152         diag_printf("Passing in wrong arguments: %d\n", argc);
1153     }
1154     /* Reset to default string */
1155     strcpy(ready_to_blow, INIT_STRING);;
1156 }
1157
1158 /* precondition: m>0 and n>0.  Let g=gcd(m,n). */
1159 int gcd(int m, int n)
1160 {
1161     int t;
1162     while(m > 0) {
1163         if(n > m) {t = m; m = n; n = t;} /* swap */
1164         m -= n;
1165     }
1166     return n;
1167  }