blob: fa4fd36a4faa589f708c99d25368e5fd57910d49 [file] [log] [blame]
/* Copyright 2015 The Chromium OS Authors. All rights reserved.
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
/* Port 80 Timer Interrupt for MEC1322 */
#include "common.h"
#include "console.h"
#include "hooks.h"
#include "lpc.h"
#include "port80.h"
#include "registers.h"
#include "task.h"
/* Fire timer interrupt every 1000 usec to check for port80 data. */
#define POLL_PERIOD_USEC 1000
/* After 30 seconds of no port 80 data, disable the timer interrupt. */
#define INTERRUPT_DISABLE_TIMEOUT_SEC 30
#define INTERRUPT_DISABLE_IDLE_COUNT (INTERRUPT_DISABLE_TIMEOUT_SEC \
* 1000000 \
/ POLL_PERIOD_USEC)
/* Count the number of consecutive interrupts with no port 80 data. */
static int idle_count;
static void port_80_interrupt_enable(void)
{
idle_count = 0;
/* Enable the interrupt. */
task_enable_irq(MEC1322_IRQ_TIMER16_1);
/* Enable and start the timer. */
MEC1322_TMR16_CTL(1) |= 1 | (1 << 5);
}
DECLARE_HOOK(HOOK_CHIPSET_RESUME, port_80_interrupt_enable, HOOK_PRIO_DEFAULT);
DECLARE_HOOK(HOOK_CHIPSET_RESET, port_80_interrupt_enable, HOOK_PRIO_DEFAULT);
static void port_80_interrupt_disable(void)
{
/* Disable the timer block. */
MEC1322_TMR16_CTL(1) &= ~1;
/* Disable the interrupt. */
task_disable_irq(MEC1322_IRQ_TIMER16_1);
}
DECLARE_HOOK(HOOK_CHIPSET_SUSPEND, port_80_interrupt_disable,
HOOK_PRIO_DEFAULT);
/*
* The port 80 interrupt will use TIMER16 instance 1 for a 1ms countdown
* timer. This timer is on GIRQ23, bit 1.
*/
static void port_80_interrupt_init(void)
{
uint32_t val = 0;
/*
* The timers are driven by a 48MHz oscillator. Prescale down to
* 1MHz. 48MHz/48 -> 1MHz
*/
val = MEC1322_TMR16_CTL(1);
val = (val & 0xFFFF) | (47 << 16);
/* Automatically restart the timer. */
val |= (1 << 3);
/* The counter should decrement. */
val &= ~(1 << 2);
MEC1322_TMR16_CTL(1) = val;
/* Set the reload value(us). */
MEC1322_TMR16_PRE(1) = POLL_PERIOD_USEC;
/* Clear the status if any. */
MEC1322_TMR16_STS(1) |= 1;
/* Clear any pending interrupt. */
MEC1322_INT_SOURCE(23) = (1 << 1);
/* Enable IRQ vector 23. */
MEC1322_INT_BLK_EN |= (1 << 23);
/* Enable the interrupt. */
MEC1322_TMR16_IEN(1) |= 1;
MEC1322_INT_ENABLE(23) = (1 << 1);
port_80_interrupt_enable();
}
DECLARE_HOOK(HOOK_INIT, port_80_interrupt_init, HOOK_PRIO_DEFAULT);
void port_80_interrupt(void)
{
int data;
MEC1322_TMR16_STS(1) = 1; /* Ack the interrupt */
if ((1 << 1) & MEC1322_INT_RESULT(23)) {
data = port_80_read();
if (data != PORT_80_IGNORE) {
idle_count = 0;
port_80_write(data);
}
}
if (++idle_count >= INTERRUPT_DISABLE_IDLE_COUNT)
port_80_interrupt_disable();
}
DECLARE_IRQ(MEC1322_IRQ_TIMER16_1, port_80_interrupt, 2);