QortalOS Brooklyn for Raspberry Pi 4
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

399 lines
13 KiB

/*
* dwc_otg_fiq_fsm.h - Finite state machine FIQ header definitions
*
* Copyright (c) 2013 Raspberry Pi Foundation
*
* Author: Jonathan Bell <[email protected]>
* 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),
};
#ifdef CONFIG_ARM64
typedef spinlock_t fiq_lock_t;
#else
typedef struct {
union {
uint32_t slock;
struct _tickets {
uint16_t owner;
uint16_t next;
} tickets;
};
} fiq_lock_t;
#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_dma_blob {
struct fiq_dma_channel channel[0];
} __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_blob *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[0];
};
#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 void fiq_fsm_spin_lock(fiq_lock_t *lock);
extern void fiq_fsm_spin_unlock(fiq_lock_t *lock);
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_ */