]> Gitweb @ Texas Instruments - Open Source Git Repositories - git.TI.com/gitweb - keystone-rtos/ibl.git/blob - src/device/c66x/c66x.c
Clean up UART prints, UART driver, etc.
[keystone-rtos/ibl.git] / src / device / c66x / c66x.c
1 /*
2  *
3  * Copyright (C) 2010 Texas Instruments Incorporated - http://www.ti.com/ 
4  * 
5  * 
6  *  Redistribution and use in source and binary forms, with or without 
7  *  modification, are permitted provided that the following conditions 
8  *  are met:
9  *
10  *    Redistributions of source code must retain the above copyright 
11  *    notice, this list of conditions and the following disclaimer.
12  *
13  *    Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the 
15  *    documentation and/or other materials provided with the   
16  *    distribution.
17  *
18  *    Neither the name of Texas Instruments Incorporated nor the names of
19  *    its contributors may be used to endorse or promote products derived
20  *    from this software without specific prior written permission.
21  *
22  *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
23  *  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
24  *  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
25  *  A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 
26  *  OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 
27  *  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 
28  *  LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
29  *  DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
30  *  THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 
31  *  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 
32  *  OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33  *
34 */
38 /************************************************************************************
39  * FILE PURPOSE: C66x Device Specific functions
40  ************************************************************************************
41  * FILE NAME: c66x.c
42  *
43  * DESCRIPTION: Implements the device specific functions for the IBL
44  *
45  * @file c66x.c
46  *
47  * @brief
48  *  This file implements the device specific functions for the IBL
49  *
50  ************************************************************************************/
51 #include "ibl.h"
52 #include "iblloc.h"
53 #include "iblcfg.h"
54 #include "device.h"
55 #include "pllapi.h"
56 #include "emif31api.h"
57 #include "pscapi.h"
58 #include "gpio.h"
59 #include "qm_api.h"
60 #include "cpdma_api.h"
61 #include "pa_api.h"
62 #include "serdes_api.h"
63 #include "net.h"
64 #include "nandhwapi.h"
65 #include "nor_api.h"
66 #include "spi_api.h"
67 #include <string.h>
68 #include <stdint.h>
69 #include "evmc66x_uart.h"
71 #define PLL_DDR_INIT_LOOPMAX 10
72 #define IBL_RESULT_CODE_STR_LEN 20
73 #define IBL_RESULT_CODE_LOC 17
75 extern cregister unsigned int DNUM;
76 #define DDR3_TEST_ENABLE
78 #ifdef DDR3_TEST_ENABLE
79 /**
80  *  @brief Simple DDR3 test
81  *
82  *  @details
83  *      This function performs a simple DDR3 test for a memory range
84  *      specified below and returns -1 for failure and 0 for success.
85  */
87 #define DDR3_TEST_START_ADDRESS 0x80000000
89 #define DDR3_TEST_END_ADDRESS   (DDR3_TEST_START_ADDRESS + (128 *1024))
91 static int32_t ddr3_memory_test (void)
92 {
93         uint32_t index, value;
95         /* Write a pattern */
96         for (index = DDR3_TEST_START_ADDRESS; index < DDR3_TEST_END_ADDRESS; index += 4) {
97                 *(volatile uint32_t *) index = (uint32_t)index;
98         }
100         /* Read and check the pattern */
101         for (index = DDR3_TEST_START_ADDRESS; index < DDR3_TEST_END_ADDRESS; index += 4) {
103                 value = *(uint32_t *) index;
105                 if (value  != index) {
106                         return -1;
107                 }
108         }
110         /* Write a pattern for complementary values */
111         for (index = DDR3_TEST_START_ADDRESS; index < DDR3_TEST_END_ADDRESS; index += 4) {
112                 *(volatile uint32_t *) index = (uint32_t)~index;
113         }
115         /* Read and check the pattern */
116         for (index = DDR3_TEST_START_ADDRESS; index < DDR3_TEST_END_ADDRESS; index += 4) {
118                 value = *(uint32_t *) index;
120                 if (value  != ~index) {
121                         return -1;
122                 }
123         }
125         return 0;
128 #endif
130 /**
131  *  @brief Determine if an address is local
132  *
133  *  @details
134  *    Examines an input address to determine if it is a local address. Using the largest
135  *    L2 size on the C6670.
136  */
137 bool address_is_local (Uint32 addr)
139     /* L2 */
140     if ((addr >= 0x00800000) && (addr < 0x00900000))
141         return (TRUE);
143     /* L1P */
144     if ((addr >= 0x00e00000) && (addr < 0x00e08000))
145         return (TRUE);
147     /* L2D */
148     if ((addr >= 0x00f00000) && (addr < 0x00f08000))
149         return (TRUE);
151     return (FALSE);
156 /**
157  * @brief  Convert a local l1d, l1p or l2 address to a global address
158  *
159  * @details
160  *  The global address is formed. If the address is not local then
161  *  the input address is returned
162  */
163 Uint32 deviceLocalAddrToGlobal (Uint32 addr)
166     if (address_is_local (addr))
167         addr = (1 << 28) | (DNUM << 24) | addr;
169     return (addr);
172         
173         
174 /**
175  * @brief
176  *   Enable the DDR
177  *
178  * @details
179  *   The DDR controller on the c66x is an emif 4.0. The controller is
180  *   initialized directly with the supplied values
181  */
182 void deviceDdrConfig (void)
184     uint32 loopcount=0;
185     int8  ddr_result_code_str[IBL_RESULT_CODE_STR_LEN] = "IBL Result code 0";
186     /* The emif registers must be made visible. MPAX mapping 2 is used */
187     DEVICE_REG_XMPAX_L(2) =  0x10000000 | 0xff;     /* replacement addr + perm*/
188     DEVICE_REG_XMPAX_H(2) =  0x2100000B;         /* base addr + seg size (64KB)*/       
189     
190     for (loopcount = 0; loopcount < PLL_DDR_INIT_LOOPMAX; loopcount++)
191     {
192         if(loopcount !=0) /*Do not call PLL sequence for the first time */
193         {
194         /* Calling MAIN, PA, DDR PLL init  */
195             if (ibl.pllConfig[ibl_MAIN_PLL].doEnable == TRUE)
196                 hwPllSetPll (MAIN_PLL, 
197                              ibl.pllConfig[ibl_MAIN_PLL].prediv,
198                                  ibl.pllConfig[ibl_MAIN_PLL].mult,
199                                  ibl.pllConfig[ibl_MAIN_PLL].postdiv);
200     
201             if (ibl.pllConfig[ibl_NET_PLL].doEnable == TRUE)
202                 hwPllSetCfgPll (DEVICE_PLL_BASE(NET_PLL),
203                                 ibl.pllConfig[ibl_NET_PLL].prediv,
204                                 ibl.pllConfig[ibl_NET_PLL].mult,
205                                 ibl.pllConfig[ibl_NET_PLL].postdiv,
206                                 ibl.pllConfig[ibl_MAIN_PLL].pllOutFreqMhz,
207                                 ibl.pllConfig[ibl_NET_PLL].pllOutFreqMhz);
208     
209             if (ibl.pllConfig[ibl_DDR_PLL].doEnable == TRUE)
210                 hwPllSetCfg2Pll (DEVICE_PLL_BASE(DDR_PLL),
211                                  ibl.pllConfig[ibl_DDR_PLL].prediv,
212                                  ibl.pllConfig[ibl_DDR_PLL].mult,
213                                  ibl.pllConfig[ibl_DDR_PLL].postdiv,
214                                  ibl.pllConfig[ibl_MAIN_PLL].pllOutFreqMhz,
215                                  ibl.pllConfig[ibl_DDR_PLL].pllOutFreqMhz);
216           }
217           
218         if (ibl.ddrConfig.configDdr != 0)
219             hwEmif4p0Enable (&ibl.ddrConfig.uEmif.emif4p0);
220         /* Init UART */
221          uart_init();
222 #ifdef DDR3_TEST_ENABLE
223         if (ddr3_memory_test() == 0) 
224         {
225             break;
226         }
227 #endif
229     }
230     if (loopcount < 10) 
231     {
232         ddr_result_code_str[IBL_RESULT_CODE_LOC] = loopcount + '0';
233     }
234     else if ((loopcount >= 10) && (loopcount < 35))
235     {
236         ddr_result_code_str[IBL_RESULT_CODE_LOC] =   loopcount + 'A';
237     }
238     else 
239     {
240         ddr_result_code_str[IBL_RESULT_CODE_LOC] =   loopcount + 'Z';
241     }
243     if (loopcount == PLL_DDR_INIT_LOOPMAX) 
244     {
245         uart_write_string("IBL: DDR INITIALIZATION FAILED",0);
246     }
247     else
248     {
249         uart_write_string("IBL: PLL and DDR Initialization Complete",0);
250     }
251     uart_write_string(ddr_result_code_str,0);
254         
257 /**
258  *  @brief  Enable EMIF25 or SPI interface to the NAND
259  *
260  */
261 int32 deviceConfigureForNand(void)
264     return (0);
268 /**
269  *  @brief
270  *      Return the base memory address for emif25 in a given chip select space
271  */
272 uint32 deviceEmif25MemBase (int32 cs)
274     switch (cs)  {
276         case 2:  return (TARGET_MEM_NAND_CS_2);
278         case 3:  return (TARGET_MEM_NAND_CS_3);
280         case 4:  return (TARGET_MEM_NAND_CS_4);
282         case 5:  return (TARGET_MEM_NAND_CS_5);
284     }
286     return (0xffffffff);
291 /**
292  *  @brief
293  *      Return the PSC number for NAND/NOR through emif. Only 6678 has the emif
294  */
295 Int32 deviceEmifPscNum (void)
297     Uint32 v;
299     v = *((Uint32 *)DEVICE_JTAG_ID_REG);
300     v &= DEVICE_JTAG_ID_MASK;
301     if (v == DEVICE_C6678_JTAG_ID_VAL)
302         return (TARGET_PWR_EMIF_C6678);
304     /* Return a negative number to indicate no PSC module is associated with NAND */
305     return (-1);
311 /**
312  *  @brief
313  *    The e-fuse mac address is loaded
314  */
315 void deviceLoadDefaultEthAddress (uint8 *maddr)
317     uint32 macA, macB;
319     /* Read the e-fuse mac address */
320     macA = *((uint32 *)0x2620110);
321     macB = *((uint32 *)0x2620114);
323     maddr[0] = (macB >>  8) & 0xff;
324     maddr[1] = (macB >>  0) & 0xff;
325     maddr[2] = (macA >> 24) & 0xff;
326     maddr[3] = (macA >> 16) & 0xff;
327     maddr[4] = (macA >>  8) & 0xff;
328     maddr[5] = (macA >>  0) & 0xff;
332 /**
333  *  @brief
334  *    Compile time queue manager information
335  */
336 #define DEVICE_NUM_RX_CPPIS     1
337 #define DEVICE_NUM_TX_CPPIS     1
338 #define DEVICE_NUM_CPPIS        (DEVICE_NUM_RX_CPPIS + DEVICE_NUM_TX_CPPIS)
340 /* The linking RAM */
341 #pragma DATA_SECTION(qm_linkram_buf, ".linkram")
342 #pragma DATA_ALIGN(qm_linkram_buf, 16)
343 uint8 qm_linkram_buf[DEVICE_NUM_CPPIS * 2 * (sizeof(uint32)/sizeof(uint8))];
346 /* The CPPI RAM */
347 #pragma DATA_SECTION(qm_cppi_buf, ".cppi")
348 #pragma DATA_ALIGN(qm_cppi_buf, 16)
349 uint8 qm_cppi_buf[QM_DESC_SIZE_BYTES * DEVICE_NUM_CPPIS];
352 /* The rx data buffers */
353 #pragma DATA_SECTION(qm_buffer, ".mac_buffer")
354 #pragma DATA_ALIGN(qm_buffer, 16)
355 uint8 qm_buffer[MAX_SIZE_STREAM_BUFFER * DEVICE_NUM_RX_CPPIS];
357 const qmConfig_t qmConfig =  {
358     (UINT32) qm_linkram_buf,
359     sizeof  (qm_cppi_buf),
360     (UINT32) qm_cppi_buf,
362     DEVICE_NUM_CPPIS,
363     DEVICE_QM_FREE_Q
364 };
366 /**
367  *  @brief
368  *      Return the queue manager memory configuration information
369  */
370 void *targetGetQmConfig (void)
372     return ((void *)&qmConfig);
375 /**
376  *  @brief
377  *      Attach a packet buffer to each descriptor and push onto the linked buffer queue
378  */
379 void targetInitQs (void)
381     int32 i;
382     qmHostDesc_t *hd;
384     for (i = 0; i < DEVICE_NUM_RX_CPPIS; i++)  {
386         hd                = hwQmQueuePop (DEVICE_QM_FREE_Q);
387         hd->buffLen       = sizeof (qm_buffer) / DEVICE_NUM_CPPIS;
388         hd->buffPtr       = (UINT32) &(qm_buffer[MAX_SIZE_STREAM_BUFFER * i]);
389         hd->nextBDPtr     = 0;
390         hd->origBufferLen = MAX_SIZE_STREAM_BUFFER;
391         hd->origBuffPtr   = hd->buffPtr;
393         hwQmQueuePush (hd, DEVICE_QM_LNK_BUF_Q, QM_DESC_SIZE_BYTES);
395     }
398     for (i = 0; i < DEVICE_NUM_TX_CPPIS; i++)  {
400         hd                = hwQmQueuePop (DEVICE_QM_FREE_Q);
401         hd->buffLen       = 0;
402         hd->buffPtr       = 0;
403         hd->nextBDPtr     = 0;
404         hd->origBufferLen = 0;
405         hd->origBuffPtr   = 0;
407         hwQmQueuePush (hd, DEVICE_QM_TX_Q, QM_DESC_SIZE_BYTES);
409     }
416 const cpdmaRxCfg_t cpdmaEthRxCfg =  {
418     DEVICE_PA_CDMA_RX_CHAN_CFG_BASE,    /* Base address of PA CPDMA rx config registers */
419     DEVICE_PA_CDMA_RX_NUM_CHANNELS,     /* Number of rx channels */
421     DEVICE_PA_CDMA_RX_FLOW_CFG_BASE,    /* Base address of PA CPDMA rx flow registers */
422     DEVICE_PA_CDMA_RX_NUM_FLOWS,        /* Number of rx flows */
424     0,                                  /* Queue manager for descriptor / buffer for received packets */
425     DEVICE_QM_LNK_BUF_Q,                /* Queue of descriptors /buffers for received packets */
427     0,                                  /* Queue manager for received packets */
428     DEVICE_QM_RCV_Q,                    /* Queue for received packets (overridden by PA)  */
430     DEVICE_RX_CDMA_TIMEOUT_COUNT        /* Teardown maximum loop wait */
431 };
434 /**
435  *  @brief
436  *      Return the cpdma configuration information
437  */
438 void *targetGetCpdmaRxConfig (void)
440     return ((void *)&cpdmaEthRxCfg);
445 const cpdmaTxCfg_t cpdmaEthTxCfg = {
447     DEVICE_PA_CDMA_GLOBAL_CFG_BASE,     /* Base address of global config registers      */
448     DEVICE_PA_CDMA_TX_CHAN_CFG_BASE,    /* Base address of PA CPDMA tx config registers */
449     DEVICE_PA_CDMA_TX_NUM_CHANNELS      /* Number of tx channels */
451 };
454 /**
455  *  @brief
456  *      return the tx cpdma configuration information
457  */
458 void *targetGetCpdmaTxConfig (void)
460     return ((void *)&cpdmaEthTxCfg);
464 /**
465  *  @brief
466  *     Configure the PA
467  */
468 void targetPaConfig (uint8 *macAddr)
470     paConfig_t     paCfg;
471     qmHostDesc_t  *hd;
472     SINT16         ret;
474     /* Filter everything except the desired mac address and the broadcast mac */
475     paCfg.mac0ms = ((uint32)macAddr[0] << 24) | ((uint32)macAddr[1] << 16) | ((uint32)macAddr[2] << 8) | (uint32)(macAddr[3]);
476     paCfg.mac0ls = ((uint32)macAddr[4] << 24) | ((uint32)macAddr[5] << 16);
478     paCfg.mac1ms = 0xffffffff;
479     paCfg.mac1ls = 0xffff0000;
481     paCfg.rxQnum = DEVICE_QM_RCV_Q;
483     /* Form the configuration command in a buffer linked to a descriptor */
484     hd = hwQmQueuePop (DEVICE_QM_LNK_BUF_Q);
485     paCfg.cmdBuf = (uint8 *)hd->origBuffPtr;
487     ret = hwPaEnable (&paCfg);
488     if (ret != 0)  {
489         iblStatus.iblFail = ibl_FAIL_CODE_PA;
490         return;
491     }
494     /* Send the command to the PA through the QM */
495     hd->softwareInfo0 = PA_MAGIC_ID;
496     hd->buffLen = 16;
497     QM_DESC_DESCINFO_SET_PKT_LEN(hd->descInfo, 16);
499     /* Set the return Queue */
500     QM_DESC_PINFO_SET_QM    (hd->packetInfo, 0);
501     QM_DESC_PINFO_SET_QUEUE (hd->packetInfo, DEVICE_QM_LNK_BUF_Q);
503     hwQmQueuePush (hd, DEVICE_QM_PA_CFG_Q, QM_DESC_SIZE_BYTES);
508 /**
509  *  @brief
510  *      Chip level SGMII serdes configuration
511  *
512  *  @details
513  *      Both lanes are always setup, regardless of the port value
514  */
515 void targetSgmiiSerdesConfig (int32 port, void *viblSgmii)
517   serdesConfig_t scfg;
518   iblSgmii_t     *sgmii = (iblSgmii_t *)viblSgmii;
520   scfg.cfg      = sgmii->auxConfig;
521   scfg.nLanes   = 2;
522   scfg.rxCfg[0] = scfg.rxCfg[1] = sgmii->rxConfig;
523   scfg.txCfg[0] = scfg.txCfg[1] = sgmii->txConfig;
525   hwSerdesConfig (TARGET_SGMII_SERDES_BASE, &scfg);
527   hwSerdesWaitLock (TARGET_SGMII_SERDES_STATUS_BASE);
532 Int32 targetMacSend (void *vptr_device, Uint8* buffer, int num_bytes) 
534     qmHostDesc_t   *hd;
535     NET_DRV_DEVICE *ptr_device = (NET_DRV_DEVICE *)vptr_device;
536     int             i;
539     /* Must always setup the descriptor to have the minimum packet length */
540     if (num_bytes < 64)
541         num_bytes = 64;
544     for (i = 0, hd = NULL; hd == NULL; i++, chipDelay32 (1000)) 
545         hd = hwQmQueuePop (DEVICE_QM_TX_Q);
546     
547     if (hd == NULL)
548         return (-1);
550     QM_DESC_DESCINFO_SET_PKT_LEN(hd->descInfo, num_bytes);
552     hd->buffLen       = num_bytes;
553     hd->origBufferLen = num_bytes;
555     hd->buffPtr     = deviceLocalAddrToGlobal((UINT32)buffer);
556     hd->origBuffPtr = deviceLocalAddrToGlobal((UINT32)buffer);
558     
559     /* Return the descriptor back to the transmit queue */
560     QM_DESC_PINFO_SET_QM(hd->packetInfo, 0);
561     QM_DESC_PINFO_SET_QUEUE(hd->packetInfo, DEVICE_QM_TX_Q);
563     hwQmQueuePush (hd, DEVICE_QM_ETH_TX_Q, QM_DESC_SIZE_BYTES);
565     return (0);
570 Int32 targetMacRcv (void *vptr_device, UINT8 *buffer)
572     Int32           pktSizeBytes; 
573     qmHostDesc_t   *hd;
574     NET_DRV_DEVICE *ptr_device = (NET_DRV_DEVICE *)vptr_device;
576     hd = hwQmQueuePop (DEVICE_QM_RCV_Q);
577     if (hd == NULL)
578         return (0);
580     pktSizeBytes = QM_DESC_DESCINFO_GET_PKT_LEN(hd->descInfo);
581     iblMemcpy ((void *)buffer, (void *)hd->buffPtr, pktSizeBytes);
583     hd->buffLen = hd->origBufferLen;
584     hd->buffPtr = hd->origBuffPtr;
586     hwQmQueuePush (hd, DEVICE_QM_LNK_BUF_Q, QM_DESC_SIZE_BYTES);
588     return (pktSizeBytes);
592 void targetFreeQs (void)
594     qmHostDesc_t   *hd;
596     do  {
598         hd = hwQmQueuePop (DEVICE_QM_FREE_Q);
600     } while (hd != NULL);
602     do  {
604         hd = hwQmQueuePop (DEVICE_QM_LNK_BUF_Q);
606     } while (hd != NULL);
607     
608     do  {
610         hd = hwQmQueuePop (DEVICE_QM_RCV_Q);
612     } while (hd != NULL);
613     
614     do  {
616         hd = hwQmQueuePop (DEVICE_QM_TX_Q);
618     } while (hd != NULL);
619     
620 }    
622 extern nandCtbl_t nandEmifCtbl;
623 /**
624  *  @brief Return the NAND interface (GPIO, EMIF25 or SPI) used based on the value
625  *         of interface
626  */
627 #ifndef EXCLUDE_NAND_GPIO
628 nandCtbl_t nandGpioCtbl =  {
630     nandHwGpioDriverInit,
631     nandHwGpioDriverReadBytes,
632     nandHwGpioDriverReadPage,
633     nandHwGpioDriverClose
635 };
636 #endif
638 #ifndef EXCLUDE_NAND_EMIF
639 extern Int32 nandHwEmifDriverInit (int32 cs, void *vdevInfo);
640 extern Int32 nandHwEmifDriverReadBytes (Uint32 block, Uint32 page, Uint32 byte, Uint32 nbytes, Uint8 *data);
641 extern Int32 nandHwEmifDriverReadPage (Uint32 block, Uint32 page, Uint8 *data);
642 extern Int32 nandHwEmifDriverClose (void);
644 nandCtbl_t nandEmifCtbl =  {
646     nandHwEmifDriverInit,
647     nandHwEmifDriverReadBytes,
648     nandHwEmifDriverReadPage,
649     nandHwEmifDriverClose
651 };
652 #endif
654 #ifndef EXCLUDE_NAND_SPI
655 nandCtbl_t nandSpiCtbl =  {
658     nandHwSpiDriverInit,
659     nandHwSpiDriverReadBytes,
660     nandHwSpiDriverReadPage,
661     nandHwSpiDriverClose
663 };
664 #endif
666 nandCtbl_t *deviceGetNandCtbl (int32 interface)
668 #ifndef EXCLUDE_NAND_GPIO
670     if (interface == ibl_PMEM_IF_GPIO)
671         return (&nandGpioCtbl);
673 #endif
675 #ifndef EXCLUDE_NAND_SPI
677     if (interface == ibl_PMEM_IF_SPI)
678         return (&nandSpiCtbl);
680 #endif
682 #ifndef EXCLUDE_NAND_EMIF
684     if ((interface >= ibl_PMEM_IF_CHIPSEL_2) && (interface <= ibl_PMEM_IF_CHIPSEL_5))
685         return (&nandEmifCtbl);
687 #endif
689     return (NULL);
694 /**
695  * @brief
696  *      Get the nor call table for the specified nor interface
697  */
699 #ifndef EXCLUDE_NOR_EMIF
700 norCtbl_t norEmifCtbl = {
701     
702     norHwEmifDriverInit,
703     norHwEmifDriverReadBytes,
704     norHwEmifDriverClose
706 };
708 #endif
710 #ifndef EXCLUDE_NOR_SPI
712 norCtbl_t norSpiCtbl = {
713     
714     norHwSpiDriverInit,
715     norHwSpiDriverReadBytes,
716     norHwSpiDriverClose
718 };
720 #endif
722 norCtbl_t *deviceGetNorCtbl (int32 interface)
725 #ifndef EXCLUDE_NOR_SPI
726     
727     if (interface == ibl_PMEM_IF_SPI)
728         return (&norSpiCtbl);
730 #endif
732 #ifndef EXCLUDE_NOR_EMIF
733    
734     if ((interface >= ibl_PMEM_IF_CHIPSEL_2) && (interface <= ibl_PMEM_IF_CHIPSEL_5))
735         return (&norEmifCtbl);
737 #endif
739     return (NULL);
742     
745     
751