aboutsummaryrefslogtreecommitdiff
path: root/include/lpc.h
blob: b641aa4e6820fafe31dd0604dd8b34bdeece1907 (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
160
161
162
163
164
// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
/* Copyright 2013-2019 IBM Corp. */

#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);
extern void lpc_init_interrupts(void);
extern void lpc_finalize_interrupts(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);

/* On P9, we have a different route for SerIRQ */
extern void lpc_serirq(uint32_t chip_id, uint32_t index);

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

/* Return the policy for a given serirq */
extern unsigned int lpc_get_irq_policy(uint32_t chip_id, uint32_t psi_idx);

/* Default bus accessors that perform error logging */
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);

/*
 * LPC bus accessors that return errors as required but do not log the failure.
 * Useful if the caller wants to test the presence of a device on the LPC bus.
 */
extern int64_t lpc_probe_write(enum OpalLPCAddressType addr_type, uint32_t addr,
			       uint32_t data, uint32_t sz);
extern int64_t lpc_probe_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 */