| /* 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); |