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