summaryrefslogtreecommitdiff
path: root/chip/mec1322/port80.c
blob: df4583ed8b0d52b5234dce02e0505efdb4f5e0cb (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
/* 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 | BIT(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 |= BIT(3);
	/* The counter should decrement. */
	val &= ~BIT(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) = BIT(1);
	/* Enable IRQ vector 23. */
	MEC1322_INT_BLK_EN |= BIT(23);
	/* Enable the interrupt. */
	MEC1322_TMR16_IEN(1) |= 1;
	MEC1322_INT_ENABLE(23) = BIT(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 (BIT(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);