]> git.kernelconcepts.de Git - karo-tx-redboot.git/blobdiff - packages/hal/arm/lpc2xxx/var/v2_0/src/lpc2xxx_misc.c
unified MX27, MX25, MX37 trees
[karo-tx-redboot.git] / packages / hal / arm / lpc2xxx / var / v2_0 / src / lpc2xxx_misc.c
index 260001200597608b137160eb826bd0c313feab0c..27e734ce2ae44c6c854ed87ee2c0c9eea01cf877 100644 (file)
@@ -50,6 +50,7 @@
 //========================================================================*/
 
 #include <pkgconf/hal.h>
+#include <pkgconf/hal_arm_lpc2xxx.h>
 
 #include <cyg/infra/cyg_type.h>         // base types
 #include <cyg/infra/cyg_trac.h>         // tracing macros
 #endif
 #include <cyg/hal/var_io.h>             // platform registers
 
-static cyg_uint32 lpc_cclk;    //CPU clock frequency
-static cyg_uint32 lpc_pclk;    //peripheral devices clock speed
-                                //(equal to, half, or quarter of CPU
-                                //clock)
+#include <cyg/infra/diag.h>     // For diagnostic printing
+
 
-cyg_uint32 hal_lpc_get_pclk(void)
-{
-    return lpc_pclk; 
-}
 // -------------------------------------------------------------------------
-// Clock support
+// eCos clock support
 // Use TIMER0
 static cyg_uint32 _period;
 
@@ -85,7 +80,7 @@ void hal_clock_initialize(cyg_uint32 period)
 {
     CYG_ADDRESS timer = CYGARC_HAL_LPC2XXX_REG_TIMER0_BASE;
 
-    period = period/(lpc_cclk/lpc_pclk);
+    period = period / (CYGNUM_HAL_ARM_LPC2XXX_CLOCK_SPEED / CYGNUM_HAL_ARM_LPC2XXX_PCLK);
 
     // Disable and reset counter
     HAL_WRITE_UINT32(timer+CYGARC_HAL_LPC2XXX_REG_TxTCR, 2);
@@ -142,7 +137,8 @@ void hal_delay_us(cyg_int32 usecs)
     // Calculate how many timer ticks the required number of
     // microseconds equate to. We do this calculation in 64 bit
     // arithmetic to avoid overflow.
-    ticks = (((cyg_uint64)usecs) * ((cyg_uint64)lpc_pclk))/1000000LL;
+    ticks = CYGNUM_HAL_ARM_LPC2XXX_PCLK;
+    ticks = (((cyg_uint64)usecs) * (ticks))/1000000LL;
     
     // Disable and reset counter
     HAL_WRITE_UINT32(timer+CYGARC_HAL_LPC2XXX_REG_TxTCR, 2);
@@ -172,35 +168,65 @@ void hal_delay_us(cyg_int32 usecs)
 // we need to read twice consecutively to get correct value
 cyg_uint32 lpc_get_vpbdiv(void)
 {   
-    cyg_uint32 div;    
+    cyg_uint32 vpbdiv_reg;
+       
     HAL_READ_UINT32(CYGARC_HAL_LPC2XXX_REG_SCB_BASE + 
-                    CYGARC_HAL_LPC2XXX_REG_VPBDIV, div);
+                    CYGARC_HAL_LPC2XXX_REG_VPBDIV, vpbdiv_reg);
     HAL_READ_UINT32(CYGARC_HAL_LPC2XXX_REG_SCB_BASE + 
-                    CYGARC_HAL_LPC2XXX_REG_VPBDIV, div);
+                    CYGARC_HAL_LPC2XXX_REG_VPBDIV, vpbdiv_reg);
 
-    return div;
+    return (vpbdiv_reg);
 }
 
-//Set the two bits in VPBDIV which control peripheral clock division
-//div must be 1,2 or 4
-void lpc_set_vpbdiv(int div)
-{   
-    cyg_uint8 orig = lpc_get_vpbdiv();
-    
-    // update VPBDIV register
+// Set the VPBDIV register. The vpb bits are 1:0 and the xclk bits are 5:4. The
+// mapping of values passed to this routine to field values is:
+//       4 = divide by 4 (register bits 00)
+//       2 = divide by 2 (register bits 10)
+//       1 = divide by 1 (register bits 01)
+// This routine assumes that only these values can occur. As they are
+// generated in the CDL hopefully this should be the case. Fortunately
+// writing 11 merely causes the previous value to be retained.
+void lpc_set_vpbdiv(int vpbdiv, int xclkdiv)
+{
+    CYG_ASSERT(((vpbdiv & 0x3) != 3) && ((xclkdiv & 0x3) != 3),
+               "illegal VPBDIV register value");
+
+    // Update VPBDIV register
+#ifdef CYGHWR_HAL_ARM_LPC2XXX_FAMILY_LPC22XX
+    HAL_WRITE_UINT32(CYGARC_HAL_LPC2XXX_REG_SCB_BASE +
+                     CYGARC_HAL_LPC2XXX_REG_VPBDIV,
+                     ((xclkdiv & 0x3) << 4) | (vpbdiv & 0x3));
+#else
     HAL_WRITE_UINT32(CYGARC_HAL_LPC2XXX_REG_SCB_BASE + 
-                     CYGARC_HAL_LPC2XXX_REG_VPBDIV, (div % 4) | (orig & 0xFC));
-    
-    lpc_pclk = lpc_cclk/div;
+                     CYGARC_HAL_LPC2XXX_REG_VPBDIV, vpbdiv & 0x3);
+#endif
 }
 
+// Perform variant setup. This optionally calls into the platform
+// HAL if it has defined HAL_PLF_HARDWARE_INIT.
 void hal_hardware_init(void)
 {
-    lpc_cclk = CYGNUM_HAL_ARM_LPC2XXX_CLOCK_SPEED;
-    lpc_set_vpbdiv(4);
+#ifdef CYGHWR_HAL_ARM_LPC2XXX_FAMILY_LPC22XX
+    lpc_set_vpbdiv(CYGNUM_HAL_ARM_LPC2XXX_VPBDIV,
+                   CYGNUM_HAL_ARM_LPC2XXX_XCLKDIV);
+#else
+    lpc_set_vpbdiv(CYGNUM_HAL_ARM_LPC2XXX_VPBDIV, 1);
+#endif
+
+    //
+    // 0xFFFFFFFF indicates that this is a non vectored ISR
+    // This is the default setting for all  interrupts
+    //
+    HAL_WRITE_UINT32(CYGARC_HAL_LPC2XXX_REG_VIC_BASE + 
+                     CYGARC_HAL_LPC2XXX_REG_VICDEFVECTADDR, 0xFFFFFFFF);
+
+#ifdef HAL_PLF_HARDWARE_INIT
+    // Perform any platform specific initializations
+    HAL_PLF_HARDWARE_INIT();
+#endif
+
     // Set up eCos/ROM interfaces
     hal_if_init();
-
 }
 
 // -------------------------------------------------------------------------
@@ -208,104 +234,227 @@ void hal_hardware_init(void)
 // should interrogate the hardware and return the IRQ vector number.
 int hal_IRQ_handler(void)
 {
-    cyg_uint32 irq_num,irq_stat;
-    // Find out which interrupt caused the IRQ
-    // picks the lowest if there are more.
-    // FIXME:try to make use of the VIC for better latency.
-    //       That will probably need changes to vectors.S and 
-    //       other int-related code
+    cyg_uint32 irq_num;
+    
     HAL_READ_UINT32(CYGARC_HAL_LPC2XXX_REG_VIC_BASE + 
-                    CYGARC_HAL_LPC2XXX_REG_VICIRQSTAT, irq_stat);
-    for (irq_num = 0; irq_num < 32; irq_num++)
-           if (irq_stat & (1<<irq_num)) break;
-    // No valid interrrupt source, treat as spurious interrupt    
-    if (irq_num < CYGNUM_HAL_ISR_MIN || irq_num > CYGNUM_HAL_ISR_MAX)
-      irq_num = CYGNUM_HAL_INTERRUPT_NONE;
+                    CYGARC_HAL_LPC2XXX_REG_VICVECTADDR, irq_num);
+    //
+    // if this is a non vectored ISR then we need to find out which interrupt 
+    // caused the IRQ
+    //      
+    if (0xFFFFFFFF == irq_num)
+    {
+        cyg_uint32 irq_stat;
+        
+        // Find out which interrupt caused the IRQ. This picks the lowest
+        // if there are more than 1.
+        HAL_READ_UINT32(CYGARC_HAL_LPC2XXX_REG_VIC_BASE + 
+                        CYGARC_HAL_LPC2XXX_REG_VICIRQSTAT, irq_stat);
+        irq_num = 0;
+        while (!(irq_stat & 0x01))
+        {
+            irq_stat >>= 1;    
+            irq_num++;
+        }
+        
+        // If not a valid interrrupt source, treat as spurious interrupt    
+        if (irq_num < CYGNUM_HAL_ISR_MIN || irq_num > CYGNUM_HAL_ISR_MAX)
+        {
+            irq_num = CYGNUM_HAL_INTERRUPT_NONE;
+        }
+    } // if (0xFFFFFFFF == irq_num)
     
-    return irq_num;
+    return (irq_num);
 }
 
 // -------------------------------------------------------------------------
 // Interrupt control
 //
 
+// Block the the interrupt associated with the vector
 void hal_interrupt_mask(int vector)
 {
     CYG_ASSERT(vector <= CYGNUM_HAL_ISR_MAX &&
                vector >= CYGNUM_HAL_ISR_MIN , "Invalid vector");
 
     HAL_WRITE_UINT32(CYGARC_HAL_LPC2XXX_REG_VIC_BASE + 
-                     CYGARC_HAL_LPC2XXX_REG_VICINTENCLEAR, 1<<vector);
+                     CYGARC_HAL_LPC2XXX_REG_VICINTENCLEAR, 1 << vector);
 }
 
+// Unblock the the interrupt associated with the vector
 void hal_interrupt_unmask(int vector)
 {
     CYG_ASSERT(vector <= CYGNUM_HAL_ISR_MAX &&
                vector >= CYGNUM_HAL_ISR_MIN , "Invalid vector");
 
     HAL_WRITE_UINT32(CYGARC_HAL_LPC2XXX_REG_VIC_BASE + 
-                     CYGARC_HAL_LPC2XXX_REG_VICINTENABLE, 1<<vector);
+                     CYGARC_HAL_LPC2XXX_REG_VICINTENABLE, 1 << vector);
 }
 
+// Acknowledge the the interrupt associated with the vector. This
+// clears the interrupt but may result in another interrupt being
+// delivered
 void hal_interrupt_acknowledge(int vector)
 {
+
+    // External interrupts have to be cleared from the EXTINT register
+    if (vector >= CYGNUM_HAL_INTERRUPT_EINT0 &&
+        vector <= CYGNUM_HAL_INTERRUPT_EINT3)
+      {
+        // Map int vector to corresponding bit (0..3)
+        vector = 1 << (vector - CYGNUM_HAL_INTERRUPT_EINT0);
+        
+        // Clear the external interrupt
+        HAL_WRITE_UINT32(CYGARC_HAL_LPC2XXX_REG_SCB_BASE + 
+                         CYGARC_HAL_LPC2XXX_REG_EXTINT, vector);
+      }
+    
+    // Acknowledge interrupt in the VIC
     HAL_WRITE_UINT32(CYGARC_HAL_LPC2XXX_REG_VIC_BASE + 
                      CYGARC_HAL_LPC2XXX_REG_VICVECTADDR, 0);  
 }
 
+// This provides control over how an interrupt signal is detected.
+// Options are between level or edge sensitive (level) and high/low
+// level or rising/falling edge triggered (up).
+//
+// This should be simple, but unfortunately on some processor revisions,
+// it trips up on two errata issues (for the LPC2294 Rev.A these are
+// EXTINT.1 and VPBDIV.1) and so on these devices a somewhat convoluted
+// sequence in order to work properly. There is nothing in the errata
+// sequence that won't work on a processor without these issues.
 void hal_interrupt_configure(int vector, int level, int up)
 {
-    cyg_uint32 mode;
-    cyg_uint32 pol;
-    // only external interrupts are configurable       
+    cyg_uint32 regval, saved_vpbdiv;
+
+    // Only external interrupts are configurable       
     CYG_ASSERT(vector <= CYGNUM_HAL_INTERRUPT_EINT3 &&
                vector >= CYGNUM_HAL_INTERRUPT_EINT0 , "Invalid vector");
-#if CYGHWR_HAL_ARM_LPC2XXX_EXTINT_ERRATA
-    // Errata sheet says VPBDIV is corrupted when accessing EXTPOL or EXTMOD
-    // Must be written as 0 and at the end restored to original value
+
+    // Map int vector to corresponding bit (0..3)
+    vector = 1 << (vector - CYGNUM_HAL_INTERRUPT_EINT0);
+    
+#ifdef CYGHWR_HAL_ARM_LPC2XXX_EXTINT_ERRATA
+    // From discussions with the Philips applications engineers on the
+    // Yahoo LPC2000 forum, it appears that in order up change both
+    // EXTMODE and EXTPOLAR, the operations have to be performed in
+    // two passes as follows:
+    // old=VPBDIV (x2),
+    //     VPBDIV=0, EXTMODE=n, VPBDIV=n, VPBDIV=0, EXTPOLAR=y, VPBDIV=y
+    // VPCDIV=old
     
+    // Save current VPBDIV register settings
+    saved_vpbdiv = lpc_get_vpbdiv();
+    
+    // Clear VPBDIV register
     HAL_WRITE_UINT32(CYGARC_HAL_LPC2XXX_REG_SCB_BASE + 
                      CYGARC_HAL_LPC2XXX_REG_VPBDIV, 0);
-#endif    
-    HAL_READ_UINT32(CYGARC_HAL_LPC2XXX_REG_SCB_BASE + 
-                    CYGARC_HAL_LPC2XXX_REG_EXTMODE, mode);
+    
+    // Read current mode and update for level (0) or edge detection (1)
     HAL_READ_UINT32(CYGARC_HAL_LPC2XXX_REG_SCB_BASE + 
-                    CYGARC_HAL_LPC2XXX_REG_EXTPOLAR, pol);
+                    CYGARC_HAL_LPC2XXX_REG_EXTMODE, regval);
+    if (level)
+      regval &= ~vector;
+    else
+      regval |= vector;
+    HAL_WRITE_UINT32(CYGARC_HAL_LPC2XXX_REG_SCB_BASE + 
+                     CYGARC_HAL_LPC2XXX_REG_EXTMODE, regval);
     
-    // map int vector to corresponding bit (0..3)
-    vector = 1 << (vector - CYGNUM_HAL_INTERRUPT_EINT0);
+    // Set VPBDIV register to same value as mode
+    HAL_WRITE_UINT32(CYGARC_HAL_LPC2XXX_REG_SCB_BASE + 
+                     CYGARC_HAL_LPC2XXX_REG_VPBDIV, regval);
     
-    // level or edge
-    if (level) {
-       mode &= ~vector;   
-    } else {
-       mode |= vector;   
-    }
+    // Clear VPBDIV register
+    HAL_WRITE_UINT32(CYGARC_HAL_LPC2XXX_REG_SCB_BASE + 
+                     CYGARC_HAL_LPC2XXX_REG_VPBDIV, 0);
     
-    // high/low or falling/rising
-    if (up) {
-       pol |= vector;   
-    } else {
-       pol &= ~vector;   
-    }
+    // Read current polarity and update for trigger level or edge
+    // level: high (1), low (0) edge: rising (1), falling (0)
+    HAL_READ_UINT32(CYGARC_HAL_LPC2XXX_REG_SCB_BASE + 
+                    CYGARC_HAL_LPC2XXX_REG_EXTPOLAR, regval);
+    if (up)
+      regval |= vector;
+    else
+      regval &= ~vector;
+    HAL_WRITE_UINT32(CYGARC_HAL_LPC2XXX_REG_SCB_BASE + 
+                     CYGARC_HAL_LPC2XXX_REG_EXTPOLAR, regval);
+    
+    // Set VPBDIV register to same value as mode
+    HAL_WRITE_UINT32(CYGARC_HAL_LPC2XXX_REG_SCB_BASE + 
+                     CYGARC_HAL_LPC2XXX_REG_VPBDIV, regval);
     
+    // Restore saved VPBDIV register
     HAL_WRITE_UINT32(CYGARC_HAL_LPC2XXX_REG_SCB_BASE + 
-                     CYGARC_HAL_LPC2XXX_REG_EXTMODE, mode);
+                     CYGARC_HAL_LPC2XXX_REG_VPBDIV, saved_vpbdiv);
+#else
+    // Read current mode and update for level (0) or edge detection (1)
+    HAL_READ_UINT32(CYGARC_HAL_LPC2XXX_REG_SCB_BASE + 
+                    CYGARC_HAL_LPC2XXX_REG_EXTMODE, regval);
+    if (level)
+      regval &= ~vector;
+    else
+      regval |= vector;
     HAL_WRITE_UINT32(CYGARC_HAL_LPC2XXX_REG_SCB_BASE + 
-                     CYGARC_HAL_LPC2XXX_REG_EXTPOLAR, pol);
+                     CYGARC_HAL_LPC2XXX_REG_EXTMODE, regval);
     
-#if CYGHWR_HAL_ARM_LPC2XXX_EXTINT_ERRATA    
-    // we know this was the original value
-    lpc_set_vpbdiv(lpc_cclk/lpc_pclk);
+    // Read current polarity and update for trigger level or edge
+    // level: high (1), low (0) edge: rising (1), falling (0)
+    HAL_READ_UINT32(CYGARC_HAL_LPC2XXX_REG_SCB_BASE + 
+                    CYGARC_HAL_LPC2XXX_REG_EXTPOLAR, regval);
+    if (up)
+      regval |= vector;
+    else
+      regval &= ~vector;
+    HAL_WRITE_UINT32(CYGARC_HAL_LPC2XXX_REG_SCB_BASE + 
+                     CYGARC_HAL_LPC2XXX_REG_EXTPOLAR, regval);
 #endif
+    // Clear any spurious interrupt that might have been generated
+    HAL_WRITE_UINT32(CYGARC_HAL_LPC2XXX_REG_SCB_BASE + 
+                     CYGARC_HAL_LPC2XXX_REG_EXTINT, vector);
 }
 
+//
+// We support up to 17 interrupt levels
+// Interrupts 0 - 15 are vectored interrupt requests. Vectored IRQs have 
+// the higher priority then non vectored IRQs, but only 16 of the 32 requests 
+// can be assigned to this category. Any of the 32 requests can be assigned 
+// to any of the 16 vectored IRQ slots, among which slot 0 has the highest 
+// priority and slot 15 has the lowest. Priority 16 indicates a non vectored
+// IRQ.
+//
 void hal_interrupt_set_level(int vector, int level)
 {
     CYG_ASSERT(vector <= CYGNUM_HAL_ISR_MAX &&
                vector >= CYGNUM_HAL_ISR_MIN , "Invalid vector");
-    CYG_ASSERT(level >= 0 && level <= 15, "Invalid level");
-
+    CYG_ASSERT(level >= 0 && level <= 16, "Invalid level");
+    
+    //
+    // If level is < 16 then this is a vectored ISR and we try to write
+    // the vector number of this ISR in the right slot of the vectored 
+    // interrupt controller
+    //
+    if (level < 16)
+    {
+        cyg_uint32 addr_offset =  level << 2;
+        cyg_uint32 reg_val;
+        
+        HAL_READ_UINT32(CYGARC_HAL_LPC2XXX_REG_VIC_BASE + 
+                        CYGARC_HAL_LPC2XXX_REG_VICVECTCNTL0 + 
+                        addr_offset, reg_val);
+        CYG_ASSERT((reg_val == 0) || (reg_val == (vector | 0x20)), 
+                   "Priority already used by another vector");
+        HAL_WRITE_UINT32(CYGARC_HAL_LPC2XXX_REG_VIC_BASE + 
+                         CYGARC_HAL_LPC2XXX_REG_VICVECTCNTL0 + 
+                         addr_offset, vector | 0x20);
+        //
+        // We do not store the adress of the ISR here but we store the
+        // vector number The hal_IRQ_handler then can faster determine
+        // the right vector number
+        //
+        HAL_WRITE_UINT32(CYGARC_HAL_LPC2XXX_REG_VIC_BASE + 
+                         CYGARC_HAL_LPC2XXX_REG_VICVECTADDR0 + 
+                         addr_offset, vector);
+    }     
 }
 
 // Use the watchdog to generate a reset
@@ -326,8 +475,39 @@ void hal_lpc_watchdog_reset(void)
                      CYGARC_HAL_LPC2XXX_REG_WDFEED, 
                      CYGARC_HAL_LPC2XXX_REG_WDFEED_MAGIC2);
     
-    while(1);
+    while(1)
+      continue;
+}
+
+#ifdef CYGPKG_DEVS_CAN_LPC2XXX
+//===========================================================================
+// Do varianat specific CAN initialisation
+//===========================================================================
+void hal_lpc_can_init(cyg_uint8 can_chan_no)
+{
+    typedef struct 
+    {
+        cyg_uint32 pin_mask;
+        cyg_uint16 reg;
+    } canpincfg;
+    
+    static const canpincfg canpincfg_tbl[] =
+    {
+        {0x00040000L, CYGARC_HAL_LPC2XXX_REG_PINSEL1},
+        {0x00014000L, CYGARC_HAL_LPC2XXX_REG_PINSEL1},
+        {0x00001800L, CYGARC_HAL_LPC2XXX_REG_PINSEL1},
+        {0x0F000000L, CYGARC_HAL_LPC2XXX_REG_PINSEL0},
+    };
+    
+    CYG_ASSERT(can_chan_no < 4, "CAN channel number out of bounds");
+    canpincfg *pincfg = (canpincfg *)&canpincfg_tbl[can_chan_no];
+    cyg_uint32 regval;
+    
+    HAL_READ_UINT32(CYGARC_HAL_LPC2XXX_REG_PIN_BASE + pincfg->reg, regval);
+    regval |= pincfg->pin_mask;
+    HAL_WRITE_UINT32(CYGARC_HAL_LPC2XXX_REG_PIN_BASE + pincfg->reg, regval);
 }
+#endif // CYGPKG_DEVS_CAN_LPC2XXX
 
 //--------------------------------------------------------------------------
 // EOF lpc_misc.c