//========================================================================*/
#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;
{
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);
// 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);
// 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();
-
}
// -------------------------------------------------------------------------
// 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
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