aboutsummaryrefslogtreecommitdiff
path: root/include/lpc.h
blob: a79d256e9c7a6afb1fe11ab4906e0b2e2a50752f (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
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
/* Copyright 2013-2014 IBM Corp.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * 	http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
 * implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#ifndef __LPC_H
#define __LPC_H

#include <opal.h>
#include <ccan/endian/endian.h>

/* Note about LPC interrupts
 *
 * LPC interrupts come in two categories:
 *
 *   - External device LPC interrupts
 *   - Error interrupts generated by the LPC controller
 *
 * The former is implemented differently depending on whether
 * you are using Murano/Venice or Naples.
 *
 * The former two chips don't have a pin to deserialize the LPC
 * SerIRQ protocol, so the only source of LPC device interrupts
 * is an external interrupt pin, which is usually connected to a
 * CPLD which deserializes SerIRQ.
 *
 * So in that case, we get external interrupts from the PSI which
 * are in effect the "OR" of all the active LPC interrupts.
 *
 * The error interrupt generated by the LPC controllers however
 * are internally routed normally to the PSI bridge and muxed with
 * the I2C interrupts.
 *
 * On Naples, there is a pin to deserialize SerIRQ, so the individual
 * LPC device interrupts (up to 19) are represented in the same status
 * and mask register as the LPC error interrupts. They are still all
 * then turned into a single XIVE interrupts in the PSI however, muxed
 * with the I2C.
 *
 * In order to more/less transparently handle this, we let individual
 * "drivers" register for specific LPC interrupts. On Naples, the handlers
 * will be called individually based on what has been demuxed by the
 * controller. On Venice/Murano, all the handlers will be called on
 * every external interrupt. The platform is responsible of calling
 * lpc_all_interrupts() from the platform external interrupt handler.
 */

/* Routines for accessing the LPC bus on Power8 */

extern void lpc_init(void);

/* Check for a default bus */
extern bool lpc_present(void);

/* Return of LPC is currently usable. This can be false if the caller
 * currently holds a lock that would make it unsafe, or the LPC bus
 * is known to be in some error condition (TBI).
 */
extern bool lpc_ok(void);

/* Handle the interrupt from the LPC controller */
extern void lpc_interrupt(uint32_t chip_id);

/* Call all external handlers */
extern void lpc_all_interrupts(uint32_t chip_id);

/* Register/deregister handler */
struct lpc_client {
	/* Callback on LPC reset */
	void (*reset)(uint32_t chip_id);

	/* Callback on LPC interrupt */
	void (*interrupt)(uint32_t chip_id, uint32_t irq_msk);
	/* Bitmask of interrupts this client is interested in
	 * Note: beware of ordering, use LPC_IRQ() macro
	 */
	uint32_t interrupts;
#define LPC_IRQ(n)	(0x80000000 >> (n))
};

extern void lpc_register_client(uint32_t chip_id, const struct lpc_client *clt);

/* Default bus accessors */
extern int64_t lpc_write(enum OpalLPCAddressType addr_type, uint32_t addr,
			 uint32_t data, uint32_t sz);
extern int64_t lpc_read(enum OpalLPCAddressType addr_type, uint32_t addr,
			uint32_t *data, uint32_t sz);

/* Mark LPC bus as used by console */
extern void lpc_used_by_console(void);

/*
 * Simplified big endian FW accessors
 */
static inline int64_t lpc_fw_read32(uint32_t *val, uint32_t addr)
{
	return lpc_read(OPAL_LPC_FW, addr, val, 4);
}

static inline int64_t lpc_fw_write32(uint32_t val, uint32_t addr)
{
	return lpc_write(OPAL_LPC_FW, addr, val, 4);
}


/*
 * Simplified Little Endian IO space accessors
 *
 * Note: We do *NOT* handle unaligned accesses
 */

static inline void lpc_outb(uint8_t data, uint32_t addr)
{
	lpc_write(OPAL_LPC_IO, addr, data, 1);
}

static inline uint8_t lpc_inb(uint32_t addr)
{
	uint32_t d32;
	int64_t rc = lpc_read(OPAL_LPC_IO, addr, &d32, 1);
	return (rc == OPAL_SUCCESS) ? d32 : 0xff;
}

static inline void lpc_outw(uint16_t data, uint32_t addr)
{
	lpc_write(OPAL_LPC_IO, addr, cpu_to_le16(data), 2);
}

static inline uint16_t lpc_inw(uint32_t addr)
{
	uint32_t d32;
	int64_t rc = lpc_read(OPAL_LPC_IO, addr, &d32, 2);
	return (rc == OPAL_SUCCESS) ? le16_to_cpu(d32) : 0xffff;
}

static inline void lpc_outl(uint32_t data, uint32_t addr)
{
	lpc_write(OPAL_LPC_IO, addr, cpu_to_le32(data), 4);
}

static inline uint32_t lpc_inl(uint32_t addr)
{
	uint32_t d32;
	int64_t rc = lpc_read(OPAL_LPC_IO, addr, &d32, 4);
	return (rc == OPAL_SUCCESS) ? le32_to_cpu(d32) : 0xffffffff;
}

#endif /* __LPC_H */