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