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