diff options
Diffstat (limited to 'roms/skiboot/include/lpc.h')
-rw-r--r-- | roms/skiboot/include/lpc.h | 164 |
1 files changed, 164 insertions, 0 deletions
diff --git a/roms/skiboot/include/lpc.h b/roms/skiboot/include/lpc.h new file mode 100644 index 000000000..b641aa4e6 --- /dev/null +++ b/roms/skiboot/include/lpc.h @@ -0,0 +1,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 */ |