/*
 * dwc_otg_fiq_fsm.h - Finite state machine FIQ header definitions
 *
 * Copyright (c) 2013 Raspberry Pi Foundation
 *
 * Author: Jonathan Bell <jonathan@raspberrypi.org>
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *	* Redistributions of source code must retain the above copyright
 *	  notice, this list of conditions and the following disclaimer.
 *	* Redistributions in binary form must reproduce the above copyright
 *	  notice, this list of conditions and the following disclaimer in the
 *	  documentation and/or other materials provided with the distribution.
 *	* Neither the name of Raspberry Pi nor the
 *	  names of its contributors may be used to endorse or promote products
 *	  derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This FIQ implements functionality that performs split transactions on
 * the dwc_otg hardware without any outside intervention. A split transaction
 * is "queued" by nominating a specific host channel to perform the entirety
 * of a split transaction. This FIQ will then perform the microframe-precise
 * scheduling required in each phase of the transaction until completion.
 *
 * The FIQ functionality has been surgically implanted into the Synopsys
 * vendor-provided driver.
 *
 */

#ifndef DWC_OTG_FIQ_FSM_H_
#define DWC_OTG_FIQ_FSM_H_

#include "dwc_otg_regs.h"
#include "dwc_otg_cil.h"
#include "dwc_otg_hcd.h"
#include <linux/kernel.h>
#include <linux/irqflags.h>
#include <linux/string.h>
#include <asm/barrier.h>

#if 0
#define FLAME_ON(x)					\
do {							\
	int gpioreg;                                    \
							\
	gpioreg = readl(__io_address(0x20200000+0x8));	\
	gpioreg &= ~(7 << (x-20)*3);			\
	gpioreg |= 0x1 << (x-20)*3;			\
	writel(gpioreg, __io_address(0x20200000+0x8));	\
							\
	writel(1<<x, __io_address(0x20200000+(0x1C)));	\
} while (0)

#define FLAME_OFF(x)					\
do {							\
	writel(1<<x, __io_address(0x20200000+(0x28)));	\
} while (0)
#else
#define FLAME_ON(x) do { } while (0)
#define FLAME_OFF(X) do { } while (0)
#endif

/* This is a quick-and-dirty arch-specific register read/write. We know that
 * writes to a peripheral on BCM2835 will always arrive in-order, also that
 * reads and writes are executed in-order therefore the need for memory barriers
 * is obviated if we're only talking to USB.
 */
#define FIQ_WRITE(_addr_,_data_) (*(volatile unsigned int *) (_addr_) = (_data_))
#define FIQ_READ(_addr_) (*(volatile unsigned int *) (_addr_))

/* FIQ-ified register definitions. Offsets are from dwc_regs_base. */
#define GINTSTS		0x014
#define GINTMSK		0x018
/* Debug register. Poll the top of the received packets FIFO. */
#define GRXSTSR		0x01C
#define HFNUM		0x408
#define HAINT		0x414
#define HAINTMSK	0x418
#define HPRT0		0x440

/* HC_regs start from an offset of 0x500 */
#define HC_START	0x500
#define HC_OFFSET	0x020

#define HC_DMA		0x14

#define HCCHAR		0x00
#define HCSPLT		0x04
#define HCINT		0x08
#define HCINTMSK	0x0C
#define HCTSIZ		0x10

#define ISOC_XACTPOS_ALL 	0b11
#define ISOC_XACTPOS_BEGIN	0b10
#define ISOC_XACTPOS_MID	0b00
#define ISOC_XACTPOS_END	0b01

#define DWC_PID_DATA2	0b01
#define DWC_PID_MDATA	0b11
#define DWC_PID_DATA1	0b10
#define DWC_PID_DATA0	0b00

typedef struct {
	volatile void* base;
	volatile void* ctrl;
	volatile void* outdda;
	volatile void* outddb;
	volatile void* intstat;
	volatile void* swirq_set;
	volatile void* swirq_clr;
} mphi_regs_t;

enum fiq_debug_level {
	FIQDBG_SCHED = (1 << 0),
	FIQDBG_INT   = (1 << 1),
	FIQDBG_ERR   = (1 << 2),
	FIQDBG_PORTHUB = (1 << 3),
};

#define TICKET_SHIFT 16
typedef struct {
	union {
		uint32_t slock;
		struct _tickets {
			uint16_t owner;
			uint16_t next;
		} tickets;
	};
} __aligned(4) fiq_lock_t;

#if defined(CONFIG_ARM64)
static inline void fiq_fsm_spin_lock(fiq_lock_t *lock)
{
	unsigned int tmp;
	fiq_lock_t lockval, newval;

	asm volatile(
	/* Atomically increment the next ticket. */
	"	prfm	pstl1strm, %3\n"
	"1:	ldaxr	%w0, %3\n"
	"	add	%w1, %w0, %w5\n"
	"	stxr	%w2, %w1, %3\n"
	"	cbnz	%w2, 1b\n"
	/* Did we get the lock? */
	"	eor	%w1, %w0, %w0, ror #16\n"
	"	cbz	%w1, 3f\n"
	/*
	 * No: spin on the owner. Send a local event to avoid missing an
	 * unlock before the exclusive load.
	 */
	"	sevl\n"
	"2:	wfe\n"
	"	ldaxrh	%w2, %4\n"
	"	eor	%w1, %w2, %w0, lsr #16\n"
	"	cbnz	%w1, 2b\n"
	/* We got the lock. Critical section starts here. */
	"3:"
	: "=&r" (lockval), "=&r" (newval), "=&r" (tmp), "+Q" (*lock)
	: "Q" (lock->tickets.owner), "I" (1 << TICKET_SHIFT)
	: "memory");
}

static inline void fiq_fsm_spin_unlock(fiq_lock_t *lock)
{
	asm volatile(
	"	stlrh	%w1, %0\n"
	: "=Q" (lock->tickets.owner)
	: "r" (lock->tickets.owner + 1)
	: "memory");
}

#else

/**
 * fiq_fsm_spin_lock() - ARMv6+ bare bones spinlock
 * Must be called with local interrupts and FIQ disabled.
 */
#if defined(CONFIG_ARCH_BCM2835) && defined(CONFIG_SMP)
static inline void fiq_fsm_spin_lock(fiq_lock_t *lock)
{
	unsigned long tmp;
	uint32_t newval;
	fiq_lock_t lockval;
	/* Nested locking, yay. If we are on the same CPU as the fiq, then the disable
	 * will be sufficient. If we are on a different CPU, then the lock protects us. */
	prefetchw(&lock->slock);
	asm volatile (
	"1:     ldrex   %0, [%3]\n"
	"       add     %1, %0, %4\n"
	"       strex   %2, %1, [%3]\n"
	"       teq     %2, #0\n"
	"       bne     1b"
	: "=&r" (lockval), "=&r" (newval), "=&r" (tmp)
	: "r" (&lock->slock), "I" (1 << TICKET_SHIFT)
	: "cc");

	while (lockval.tickets.next != lockval.tickets.owner) {
		wfe();
		lockval.tickets.owner = READ_ONCE(lock->tickets.owner);
	}
	smp_mb();
}
#else
static inline void fiq_fsm_spin_lock(fiq_lock_t *lock) { }
#endif

/**
 * fiq_fsm_spin_unlock() - ARMv6+ bare bones spinunlock
 */
#if defined(CONFIG_ARCH_BCM2835) && defined(CONFIG_SMP)
static inline void fiq_fsm_spin_unlock(fiq_lock_t *lock)
{
	smp_mb();
	lock->tickets.owner++;
	dsb_sev();
}
#else
static inline void fiq_fsm_spin_unlock(fiq_lock_t *lock) { }
#endif

#endif

struct fiq_state;

extern void _fiq_print (enum fiq_debug_level dbg_lvl, volatile struct fiq_state *state, char *fmt, ...);
#if 0
#define fiq_print _fiq_print
#else
#define fiq_print(x, y, ...)
#endif

extern bool fiq_enable, fiq_fsm_enable;
extern ushort nak_holdoff;

/**
 * enum fiq_fsm_state - The FIQ FSM states.
 *
 * This is the "core" of the FIQ FSM. Broadly, the FSM states follow the
 * USB2.0 specification for host responses to various transaction states.
 * There are modifications to this host state machine because of a variety of
 * quirks and limitations in the dwc_otg hardware.
 *
 * The fsm state is also used to communicate back to the driver on completion of
 * a split transaction. The end states are used in conjunction with the interrupts
 * raised by the final transaction.
 */
enum fiq_fsm_state {
	/* FIQ isn't enabled for this host channel */
	FIQ_PASSTHROUGH = 0,
	/* For the first interrupt received for this channel,
	 * the FIQ has to ack any interrupts indicating success. */
	FIQ_PASSTHROUGH_ERRORSTATE = 31,
	/* Nonperiodic state groups */
	FIQ_NP_SSPLIT_STARTED = 1,
	FIQ_NP_SSPLIT_RETRY = 2,
	/* TT contention - working around hub bugs */
	FIQ_NP_SSPLIT_PENDING = 33,
	FIQ_NP_OUT_CSPLIT_RETRY = 3,
	FIQ_NP_IN_CSPLIT_RETRY = 4,
	FIQ_NP_SPLIT_DONE = 5,
	FIQ_NP_SPLIT_LS_ABORTED = 6,
	/* This differentiates a HS transaction error from a LS one
	 * (handling the hub state is different) */
	FIQ_NP_SPLIT_HS_ABORTED = 7,

	/* Periodic state groups */
	/* Periodic transactions are either started directly by the IRQ handler
	 * or deferred if the TT is already in use.
	 */
	FIQ_PER_SSPLIT_QUEUED = 8,
	FIQ_PER_SSPLIT_STARTED = 9,
	FIQ_PER_SSPLIT_LAST = 10,


	FIQ_PER_ISO_OUT_PENDING = 11,
	FIQ_PER_ISO_OUT_ACTIVE = 12,
	FIQ_PER_ISO_OUT_LAST = 13,
	FIQ_PER_ISO_OUT_DONE = 27,

	FIQ_PER_CSPLIT_WAIT = 14,
	FIQ_PER_CSPLIT_NYET1 = 15,
	FIQ_PER_CSPLIT_BROKEN_NYET1 = 28,
	FIQ_PER_CSPLIT_NYET_FAFF = 29,
	/* For multiple CSPLITs (large isoc IN, or delayed interrupt) */
	FIQ_PER_CSPLIT_POLL = 16,
	/* The last CSPLIT for a transaction has been issued, differentiates
	 * for the state machine to queue the next packet.
	 */
	FIQ_PER_CSPLIT_LAST = 17,

	FIQ_PER_SPLIT_DONE = 18,
	FIQ_PER_SPLIT_LS_ABORTED = 19,
	FIQ_PER_SPLIT_HS_ABORTED = 20,
	FIQ_PER_SPLIT_NYET_ABORTED = 21,
	/* Frame rollover has occurred without the transaction finishing. */
	FIQ_PER_SPLIT_TIMEOUT = 22,

	/* FIQ-accelerated HS Isochronous state groups */
	FIQ_HS_ISOC_TURBO = 23,
	/* For interval > 1, SOF wakes up the isochronous FSM */
	FIQ_HS_ISOC_SLEEPING = 24,
	FIQ_HS_ISOC_DONE = 25,
	FIQ_HS_ISOC_ABORTED = 26,
	FIQ_DEQUEUE_ISSUED = 30,
	FIQ_TEST = 32,
};

struct fiq_stack {
	int magic1;
	uint8_t stack[2048];
	int magic2;
};


/**
 * struct fiq_dma_info - DMA bounce buffer utilisation information (per-channel)
 * @index:	Number of slots reported used for IN transactions / number of slots
 *			transmitted for an OUT transaction
 * @slot_len[6]: Number of actual transfer bytes in each slot (255 if unused)
 *
 * Split transaction transfers can have variable length depending on other bus
 * traffic. The OTG core DMA engine requires 4-byte aligned addresses therefore
 * each transaction needs a guaranteed aligned address. A maximum of 6 split transfers
 * can happen per-frame.
 */
struct fiq_dma_info {
	u8 index;
	u8 slot_len[6];
};

struct fiq_split_dma_slot {
	u8 buf[188];
} __attribute__((packed));

struct fiq_dma_channel {
	struct fiq_split_dma_slot index[6];
} __attribute__((packed));

/**
 * struct fiq_hs_isoc_info - USB2.0 isochronous data
 * @iso_frame:	Pointer to the array of OTG URB iso_frame_descs.
 * @nrframes:	Total length of iso_frame_desc array
 * @index:	Current index (FIQ-maintained)
 * @stride:	Interval in uframes between HS isoc transactions
 */
struct fiq_hs_isoc_info {
	struct dwc_otg_hcd_iso_packet_desc *iso_desc;
	unsigned int nrframes;
	unsigned int index;
	unsigned int stride;
};

/**
 * struct fiq_channel_state - FIQ state machine storage
 * @fsm:	Current state of the channel as understood by the FIQ
 * @nr_errors:	Number of transaction errors on this split-transaction
 * @hub_addr:   SSPLIT/CSPLIT destination hub
 * @port_addr:  SSPLIT/CSPLIT destination port - always 1 if single TT hub
 * @nrpackets:  For isoc OUT, the number of split-OUT packets to transmit. For
 * 		split-IN, number of CSPLIT data packets that were received.
 * @hcchar_copy:
 * @hcsplt_copy:
 * @hcintmsk_copy:
 * @hctsiz_copy:	Copies of the host channel registers.
 * 			For use as scratch, or for returning state.
 *
 * The fiq_channel_state is state storage between interrupts for a host channel. The
 * FSM state is stored here. Members of this structure must only be set up by the
 * driver prior to enabling the FIQ for this host channel, and not touched until the FIQ
 * has updated the state to either a COMPLETE state group or ABORT state group.
 */

struct fiq_channel_state {
	enum fiq_fsm_state fsm;
	unsigned int nr_errors;
	unsigned int hub_addr;
	unsigned int port_addr;
	/* Hardware bug workaround: sometimes channel halt interrupts are
	 * delayed until the next SOF. Keep track of when we expected to get interrupted. */
	unsigned int expected_uframe;
	/* number of uframes remaining (for interval > 1 HS isoc transfers) before next transfer */
	unsigned int uframe_sleeps;
	/* in/out for communicating number of dma buffers used, or number of ISOC to do */
	unsigned int nrpackets;
	struct fiq_dma_info dma_info;
	struct fiq_hs_isoc_info hs_isoc_info;
	/* Copies of HC registers - in/out communication from/to IRQ handler
	 * and for ease of channel setup. A bit of mungeing is performed - for
	 * example the hctsiz.b.maxp is _always_ the max packet size of the endpoint.
	 */
	hcchar_data_t hcchar_copy;
	hcsplt_data_t hcsplt_copy;
	hcint_data_t hcint_copy;
	hcintmsk_data_t hcintmsk_copy;
	hctsiz_data_t hctsiz_copy;
	hcdma_data_t hcdma_copy;
};

/**
 * struct fiq_state - top-level FIQ state machine storage
 * @mphi_regs:		virtual address of the MPHI peripheral register file
 * @dwc_regs_base:	virtual address of the base of the DWC core register file
 * @dma_base:		physical address for the base of the DMA bounce buffers
 * @dummy_send:		Scratch area for sending a fake message to the MPHI peripheral
 * @gintmsk_saved:	Top-level mask of interrupts that the FIQ has not handled.
 * 			Used for determining which interrupts fired to set off the IRQ handler.
 * @haintmsk_saved:	Mask of interrupts from host channels that the FIQ did not handle internally.
 * @np_count:		Non-periodic transactions in the active queue
 * @np_sent:		Count of non-periodic transactions that have completed
 * @next_sched_frame:	For periodic transactions handled by the driver's SOF-driven queuing mechanism,
 * 			this is the next frame on which a SOF interrupt is required. Used to hold off
 * 			passing SOF through to the driver until necessary.
 * @channel[n]:		Per-channel FIQ state. Allocated during init depending on the number of host
 * 			channels configured into the core logic.
 *
 * This is passed as the first argument to the dwc_otg_fiq_fsm top-level FIQ handler from the asm stub.
 * It contains top-level state information.
 */
struct fiq_state {
	fiq_lock_t lock;
	mphi_regs_t mphi_regs;
	void *dwc_regs_base;
	dma_addr_t dma_base;
	struct fiq_dma_channel *fiq_dmab;
	void *dummy_send;
	dma_addr_t dummy_send_dma;
	gintmsk_data_t gintmsk_saved;
	haintmsk_data_t haintmsk_saved;
	int mphi_int_count;
	unsigned int fiq_done;
	unsigned int kick_np_queues;
	unsigned int next_sched_frame;
#ifdef FIQ_DEBUG
	char * buffer;
	unsigned int bufsiz;
#endif
	struct fiq_channel_state channel[];
};

#ifdef CONFIG_ARM64

#ifdef local_fiq_enable
#undef local_fiq_enable
#endif

#ifdef local_fiq_disable
#undef local_fiq_disable
#endif

extern void local_fiq_enable(void);

extern void local_fiq_disable(void);

#endif

extern int fiq_fsm_too_late(struct fiq_state *st, int n);

extern int fiq_fsm_tt_in_use(struct fiq_state *st, int num_channels, int n);

extern void dwc_otg_fiq_fsm(struct fiq_state *state, int num_channels);

extern void dwc_otg_fiq_nop(struct fiq_state *state);

#endif /* DWC_OTG_FIQ_FSM_H_ */
