mirror of https://gitee.com/openkylin/qemu.git
165 lines
4.9 KiB
C
165 lines
4.9 KiB
C
// 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 */
|