summaryrefslogtreecommitdiff
path: root/cpu/amd/geode_lx/gplvsa_ii/sysmgr/mfgpt.c
diff options
context:
space:
mode:
Diffstat (limited to 'cpu/amd/geode_lx/gplvsa_ii/sysmgr/mfgpt.c')
-rwxr-xr-xcpu/amd/geode_lx/gplvsa_ii/sysmgr/mfgpt.c500
1 files changed, 500 insertions, 0 deletions
diff --git a/cpu/amd/geode_lx/gplvsa_ii/sysmgr/mfgpt.c b/cpu/amd/geode_lx/gplvsa_ii/sysmgr/mfgpt.c
new file mode 100755
index 0000000..27b3cdf
--- /dev/null
+++ b/cpu/amd/geode_lx/gplvsa_ii/sysmgr/mfgpt.c
@@ -0,0 +1,500 @@
+/*
+* Copyright (c) 2006-2008 Advanced Micro Devices,Inc. ("AMD").
+*
+* This library is free software; you can redistribute it and/or modify
+* it under the terms of the GNU Lesser General Public License as
+* published by the Free Software Foundation; either version 2.1 of the
+* License, or (at your option) any later version.
+*
+* This code is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+* Lesser General Public License for more details.
+
+* You should have received a copy of the GNU Lesser General
+* Public License along with this library; if not, write to the
+* Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+* Boston, MA 02111-1307 USA
+*/
+
+//*****************************************************************************
+//* This file contains code specific to CS5536 MFGPTs
+//*****************************************************************************
+
+
+#include "VSA2.H"
+#include "SYSMGR.H"
+#include "PROTOS.H"
+#include "MDD.H"
+#include "MAPPER.H"
+#include "TIMER.H"
+
+// External variables:
+extern ULONG MDD_Base;
+
+// Local variables:
+USHORT MFGPT_Base=0;
+ULONG MFGPT_LBAR[2];
+
+
+
+// NOTE: Timers that use the 32KHz clock interfere with LPC DMA (see PBZ 709)
+
+ // Clock Prescalar Period
+#define TIMER_SETUP (MFGPT_SCALE_1K | MFGPT_CMP1MODE | MFGPT_CLK_SEL) // 14 MHz 1024 71 us
+#define STDBY_SETUP (MFGPT_SCALE_32 | MFGPT_CMP1MODE) // 32 KHz 32 1 ms
+#define PWM_SETUP (MFGPT_SCALE_32 | MFGPT_CMP1GE) // 32 KHz 32 1 ms
+TIMERS TimerInfo[] = {
+// # Z Mapper Setup
+// Save for OS (Linux) use { 0, Z_IRQ_MFGPT_04, PWM_SETUP},
+ { 1, Z_IRQ_MFGPT_15, PWM_SETUP},
+ { 2, Z_IRQ_MFGPT_26, PWM_SETUP},
+ { 3, Z_IRQ_MFGPT_37, TIMER_SETUP},
+// Save for OS (Linux) use { 4, Z_IRQ_MFGPT_04, TIMER_SETUP},
+ { 5, Z_IRQ_MFGPT_15, TIMER_SETUP},
+ { 6, Z_IRQ_MFGPT_26, STDBY_SETUP},
+ { 7, Z_IRQ_MFGPT_37, PWM_SETUP},
+};
+#define NUM_MFGPTS (sizeof(TimerInfo)/sizeof(TIMERS)) // Number of MFGPTs available
+
+//************************************************************************************
+// Saves the MFGPT LBAR setting and enables the LBAR
+//************************************************************************************
+void Enable_MFGPT_LBAR()
+{ ULONG Tmp;
+ int i;
+
+ // Get MFGPT LBAR
+ Read_MSR(MDD_Base + MSR_LBAR_MFGPT, MFGPT_LBAR);
+
+ // Has the MFGPT base address changed?
+ if (MFGPT_Base != (USHORT)MFGPT_LBAR[0]) {
+ // Yes, record the new base address of MFGPT
+ MFGPT_Base = (USHORT)MFGPT_LBAR[0];
+
+ // Update the individual timer base addresses
+ for (i = 0; i < NUM_MFGPTS; i++) {
+ TimerInfo[i].TimerBase = MFGPT_Base + TimerInfo[i].Timer*MFGPT_OFFSET;
+ }
+ }
+
+ // Save the current LBAR enable
+ Tmp = MFGPT_LBAR[1];
+
+ // Enable the LBAR
+ MFGPT_LBAR[1] |= LBAR_EN;
+ Write_MSR(MDD_Base + MSR_LBAR_MFGPT, MFGPT_LBAR);
+ MFGPT_LBAR[1] = Tmp;
+
+}
+
+
+//************************************************************************************
+// Restores the MFGPT LBAR
+//************************************************************************************
+void Restore_MFGPT_LBAR()
+{
+ // Restore MFGPT LBAR
+ Write_MSR(MDD_Base + MSR_LBAR_MFGPT, MFGPT_LBAR);
+}
+
+
+//************************************************************************************
+// Checks for MFGPT events
+// Returns a mask of which timer(s) have expired
+//************************************************************************************
+USHORT CS5536_MFGPT_Handler(void)
+{ USHORT Setup, Status, Accum_Status=0, i;
+ register TIMERS * TimerPtr;
+
+ Enable_MFGPT_LBAR();
+
+ for (i = 0; i < NUM_MFGPTS; i++) {
+
+ TimerPtr = &TimerInfo[i];
+
+ // Get timer status
+ Setup = TimerPtr->TimerBase + MFGPT_SETUP;
+ Status = in_16(Setup);
+ if (Status & MFGPT_INITED) {
+ out_16(Setup, Status);
+ }
+
+ // Check if MFGPT is reserved for EVENT_PWM
+ if(TimerPtr->Setup == PWM_SETUP) {
+ continue;
+ }
+
+ // If timer is enabled and expired...
+ Status &= MFGPT_ENABLE | MFGPT_COMPARE1;
+ if (Status == (MFGPT_ENABLE | MFGPT_COMPARE1)) {
+ // disable the timer
+ out_16(Setup, Status & (~MFGPT_ENABLE));
+ // record timer event
+ Accum_Status |= TimerPtr->Mask;
+ }
+ }
+
+ // Restore the MFGPT LBAR
+ Restore_MFGPT_LBAR();
+
+ return Accum_Status;
+}
+
+//************************************************************************************
+// Initializes a single MFGPT
+//************************************************************************************
+void Init_Timer(TIMERS * TimerPtr)
+{ USHORT Setup, TimerKHz;
+ ULONG MicrosPerCount;
+
+ TimerPtr->TimerBase = MFGPT_Base + TimerPtr->Timer*MFGPT_OFFSET;
+
+ TimerPtr->Mask = 1 << TimerPtr->Timer;
+
+ Setup = TimerPtr->Setup;
+
+ // Compute microseconds / tick
+ MicrosPerCount = 1000L << (Setup & 0xF);
+ TimerKHz = 32;
+ if (Setup & MFGPT_CLK_SEL) {
+ TimerKHz = 14318;
+ }
+ TimerPtr->Period = (USHORT)(MicrosPerCount / TimerKHz);
+
+ // Initialize the timer
+ out_16(TimerPtr->TimerBase + MFGPT_SETUP, Setup);
+
+ // NOTE: Counter resets to 0 when Counter == Comparator2 register
+ out_32(TimerPtr->TimerBase + MFGPT_CMP1, 0xFFFFFFFF);
+
+}
+
+//***********************************************************************
+// Marks a MFGPT as available
+//***********************************************************************
+void pascal MarkTimerAvailable(USHORT Timer)
+{ USHORT i;
+
+ for (i = 0; i < NUM_MFGPTS; i++) {
+ if (TimerInfo[i].Timer == Timer) {
+ TimerInfo[i].Interval = 0x00000000;
+ return;
+ }
+ }
+
+ Log_Error("Invalid timer: %d.", Timer);
+}
+
+
+//************************************************************************************
+// Initializes the MFGPT timers to be used for EVENT_TIMER
+//************************************************************************************
+void Init_MFGPT(void)
+{ ULONG MsrAddr, IRQ_Enables;
+ USHORT i;
+ register TIMERS * TimerPtr;
+
+ Enable_MFGPT_LBAR();
+
+ // Get current MFGPT IRQ mask
+ MsrAddr = MDD_Base;
+ (USHORT)MsrAddr = MSR_MFGPT_IRQ;
+ IRQ_Enables = Read_MSR_LO(MsrAddr);
+
+ for (i = 0; i < NUM_MFGPTS; i++) {
+
+ TimerPtr = &TimerInfo[i];
+
+ // Initialize a MFGPT
+ Init_Timer(TimerPtr);
+
+ // Mark timer as available
+ MarkTimerAvailable(TimerPtr->Timer);
+
+ // Route Compare 1 events to SMI
+ IRQZ_Mapper(TimerPtr->Mapper, 2);
+
+ // Enable timer event
+ IRQ_Enables |= TimerPtr->Mask;
+
+ }
+
+
+ // Restore the MFGPT LBAR
+ Restore_MFGPT_LBAR();
+
+
+ // Enable MFGPT SMIs
+ Write_MSR_LO(MsrAddr, IRQ_Enables);
+
+}
+
+
+
+//***********************************************************************
+// Disables a millisecond timer
+//***********************************************************************
+void DisableMsTimer_5536(USHORT Timer)
+{ USHORT Setup, i;
+
+ Enable_MFGPT_LBAR();
+
+
+ for (i = 0; i < NUM_MFGPTS; i++) {
+ if (TimerInfo[i].Timer == Timer) {
+ // Mark timer as available
+ MarkTimerAvailable(Timer);
+
+ // Clear timer event and disable timer
+ Setup = TimerInfo[i].TimerBase + MFGPT_SETUP;
+ out_16(Setup, MFGPT_COMPARE1 | MFGPT_COMPARE2);
+ break;
+ }
+ }
+
+ // Restore the MFGPT LBAR
+ Restore_MFGPT_LBAR();
+
+}
+
+
+
+//***********************************************************************
+// Allocates a h/w timer for the specified interval
+// Returns the timer # allocated else 0xFFFF
+//***********************************************************************
+USHORT AllocateTimer_5536(ULONG Interval, UCHAR Attributes)
+{ UCHAR i, Exact = 0, Unused=0, Compat=0, Mask;
+ register TIMERS * TimerPtr;
+
+ // Find the MFGPT whose timebase is the best-match
+ for (i = 0; i < NUM_MFGPTS; i++) {
+ TimerPtr = &TimerInfo[i];
+
+ // Don't use timers reserved for EVENT_PWM
+ if (TimerPtr->Setup == PWM_SETUP) {
+ continue;
+ }
+
+ if (Attributes & (FOR_STANDBY >> 24)) {
+ // Timers used for PM must be in the 32 KHz domain
+ if (TimerPtr->Setup & MFGPT_CLK_SEL) {
+ continue;
+ }
+ } else {
+ if (TimerPtr->Timer >= 6) {
+ // Timers 6 & 7 are reserved for PM
+ continue;
+ }
+ }
+ Mask = (UCHAR)TimerPtr->Mask;
+
+ // Is timer unused?
+ if (TimerPtr->Interval == 0x00000000) {
+ Unused |= Mask;
+ }
+
+ // Is timebase a perfect match?
+ if ((Interval*1000 % TimerPtr->Period) == 0) {
+ Exact |= Mask;
+ }
+
+ // Is timebase compatible?
+ if (Interval*1000 >= TimerPtr->Period) {
+ Compat |= Mask;
+ }
+ }
+
+ // Is there an UNUSED timer whose timebase is an exact match?
+ if (!(Mask = (Unused & Exact))) {
+ // No, is there an UNUSED timer that is compatible?
+ if (!(Mask = (Unused & Compat))) {
+ // No, is there ANY timer whose timebase is an exact match?
+ if (!(Mask = Exact)) {
+ // No, is there ANY timer that is compatible?
+ if (!(Mask = Compat)) {
+ // No timers are available
+ return 0xFFFF;
+ }
+ }
+ }
+ }
+
+ // Return the MFGPT index
+ Mask = 1 << BitScanReverse(Mask);
+ for (i = 0; i < NUM_MFGPTS; i++) {
+ if (TimerInfo[i].Mask == Mask) {
+ return i;
+ }
+ }
+ return 0xFFFF;
+}
+
+
+//***********************************************************************
+// Enables a millisecond timer
+//***********************************************************************
+UCHAR EnableMsTimer_5536(ULONG Interval, UCHAR Attributes)
+{ USHORT i, Count, TimerBase;
+ ULONG MsrAddr, IRQ_Enables, TotalCounts, Roundoff;
+ register TIMERS * TimerPtr;
+
+ // Get current MFGPT IRQ mask
+ MsrAddr = MDD_Base;
+ (USHORT)MsrAddr = MSR_MFGPT_IRQ;
+ IRQ_Enables = Read_MSR_LO(MsrAddr);
+
+ // Allocate a h/w timer
+ i = AllocateTimer_5536(Interval, Attributes);
+ if (i == 0xFFFF) {
+ Log_Error("Attempt to enable %d ms timer interval failed", Interval);
+ return 0;
+ }
+
+ TimerPtr = &TimerInfo[i];
+
+ // A timer was allocated, is it already in use?
+ if (TimerPtr->Interval) {
+ // Yes, does it need to be reprogrammed?
+ if (Interval > TimerPtr->Interval) {
+ // No, but since the timer has already been running for an
+ // indeterminate period, the 1st interval will be shortened.
+ return (UCHAR)TimerPtr->Timer;
+ }
+ }
+
+
+ if (Interval) {
+
+ Enable_MFGPT_LBAR();
+
+ // Get I/O address of this timer
+ TimerBase = TimerPtr->TimerBase;
+
+ // The MFGPT document specifies the following sequence in order
+ // to prevent any spurious resets, interrupt, or output events:
+ // - Set Counter Enable bit to 0.
+ // - Clear SMI enable in MSR_MFGPT_IRQ
+ // - Update Count as desired
+ // - When updates are completed, clear event bits
+ // - Set SMI enable in MSR_MFGPT_IRQ
+ // - Set Counter Enable bit to 1
+
+ // Does this timer need to be initialized again (e.g. after S3) ?
+ if (!(in_16(TimerBase + MFGPT_SETUP) & MFGPT_INITED)) {
+ // Initialize the timer
+ Init_Timer(TimerPtr);
+ }
+
+ // Set Counter Enable to 0
+ out_16(TimerBase + MFGPT_SETUP, 0);
+
+ // Disable SMIs
+ Write_MSR_LO(MsrAddr, 0x00000000);
+
+ // Scale the interval appropriately
+ Roundoff = 0;
+ if (TimerPtr->Period < 1000) {
+ Roundoff = TimerPtr->Period;
+ }
+ TotalCounts = (Interval*1000 + Roundoff)/TimerPtr->Period;
+ Count = (USHORT)TotalCounts;
+ if (TotalCounts & 0xFFFF0000) {
+ Count = 0xFFFF;
+ }
+
+ // Record the timer's interval
+ TimerPtr->Interval = ((ULONG)Count * TimerPtr->Period)/1000;
+
+ // Zero the counter
+ out_16(TimerBase + MFGPT_COUNTER, 0x0000);
+
+ // Set Comparator1 to Count and Comparator2 to 0xFFFF
+ out_32(TimerBase + MFGPT_CMP1, 0xFFFF0000L | Count);
+
+ // Clear timer event(s)
+ out_16(TimerBase + MFGPT_SETUP, MFGPT_COMPARE1 | MFGPT_COMPARE2);
+
+ // Re-enable SMIs
+ Write_MSR_LO(MsrAddr, IRQ_Enables);
+
+ // Set Counter Enable to 1
+ out_16(TimerBase + MFGPT_SETUP, MFGPT_ENABLE);
+
+ // Restore the MFGPT LBAR
+ Restore_MFGPT_LBAR();
+ }
+
+ return (UCHAR)TimerPtr->Timer;
+}
+
+
+//***********************************************************************
+// Configures a MFGPT as a PWM signal generator
+//***********************************************************************
+void pascal Program_PWM(UCHAR Gpio, UCHAR Duty, USHORT Rate, UCHAR EnableFlag)
+{ USHORT i, Timer;
+ register TIMERS * TimerPtr;
+
+ // Determine the MFGPT associated with the specified GPIO
+ switch (Gpio) {
+ case 5:
+ Timer = 0;
+ break;
+ case 6:
+ Timer = 1;
+ break;
+ case 7:
+ Timer = 2;
+ break;
+ case 27:
+ Timer = 7;
+ break;
+ default:
+ Log_Error("EVENT_PWM is not supported on GPIO%d", Gpio);
+ return;
+ }
+
+ // Program the MFGPT to the specified rate and duty cycle
+ for (i = 0; i < NUM_MFGPTS; i++) {
+
+ TimerPtr = &TimerInfo[i];
+
+ if (TimerPtr->Timer == Timer) {
+
+ // Check if MFGPT is reserved for EVENT_PWM
+ if(TimerPtr->Setup != PWM_SETUP) {
+ continue;
+ }
+
+ // Enable the MFGPT LBAR
+ Enable_MFGPT_LBAR();
+
+ // Clear this timer's SETUP register in case it has been used previously as a timer
+ // Write_MSR_LO(MDD_Base + MSR_MFGPT_CLR_SETUP, TimerPtr->Mask);
+ // out_16(TimerPtr->TimerBase + MFGPT_SETUP, PWM_SETUP);
+
+
+ // Clamp duty cycle to 100%
+ if (Duty > 100) {
+ Duty = 100;
+ }
+
+ // Program the Count registers
+ out_16(TimerPtr->TimerBase + MFGPT_COUNTER, 0x0000);
+ out_16(TimerPtr->TimerBase + MFGPT_CMP1, (USHORT)(((ULONG)Rate * Duty)/100));
+ out_16(TimerPtr->TimerBase + MFGPT_CMP2, Rate);
+
+ // Enable the counter
+ out_16(TimerPtr->TimerBase + MFGPT_SETUP, EnableFlag ? MFGPT_ENABLE: 0);
+
+ Restore_MFGPT_LBAR();
+
+ return;
+ }
+ }
+
+ Log_Error("EVENT_PWM on GPIO%d failed because MFGPT%d is reserved.", Gpio, Timer);
+}
+