blob: fe134f921fb9ff9265e2923d78f770306d76da9e [file] [log] [blame]
/*
* Copyright 2017, Data61
* Commonwealth Scientific and Industrial Research Organisation (CSIRO)
* ABN 41 687 119 230.
*
* This software may be distributed and modified according to the terms of
* the BSD 2-Clause license. Note that NO WARRANTY is provided.
* See "LICENSE_BSD2.txt" for details.
*
* @TAG(DATA61_BSD)
*/
#ifndef _EHCI_EHCI_H_
#define _EHCI_EHCI_H_
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <usb/usb_host.h>
#include <usb/drivers/usbhub.h>
/*******************
**** Registers ****
*******************/
struct ehci_host_cap {
uint8_t caplength; /* +0x00 */
uint8_t res0[1];
uint16_t hciversion; /* +0x02 */
#define EHCI_HCS_N_PORTS(x) (((x) & 0xf) >> 0)
uint32_t hcsparams; /* +0x04 */
#define EHCI_HCC_EECP(x) (((x) >> 8) & 0xff)
#define EHCI_HCC_ISOTHRES(x) (((x) >> 4) & 0xf)
#define EHCI_HCC_PARK BIT(2)
#define EHCI_HCC_PFRAMELIST BIT(1)
#define EHCI_HCC_64BIT BIT(0)
uint32_t hccparams; /* +0x08 */
uint32_t hcsp_portroute; /* +0x0C */
uint32_t res1[4];
/* OTG only */
uint16_t dciversion; /* +0x20 */
uint32_t dccparams; /* +0x24 */
};
struct ehci_host_op {
#define EHCICMD_IRQTHRES(x) (((x) & 0xff) * BIT(16))
#define EHCICMD_IRQTHRES_MASK EHCICMD_IRQTHRES(0xff)
#define EHCICMD_ASYNC_PARK BIT(11)
#define EHCICMD_ASYNC_PARKM (((x) & 0x3) * BIT( 8))
#define EHCICMD_LIGHT_RST BIT(7)
#define EHCICMD_ASYNC_DB BIT(6)
#define EHCICMD_ASYNC_EN BIT(5)
#define EHCICMD_PERI_EN BIT(4)
#define EHCICMD_LIST_S1024 (0x0 * BIT(2))
#define EHCICMD_LIST_S512 (0x1 * BIT(2))
#define EHCICMD_LIST_S256 (0x2 * BIT(2))
#define EHCICMD_LIST_SMASK (0x3 * BIT(2))
#define EHCICMD_HCRESET BIT(1)
#define EHCICMD_RUNSTOP BIT(0)
uint32_t usbcmd; /* +0x00 */
#define EHCISTS_ASYNC_EN BIT(15)
#define EHCISTS_PERI_EN BIT(14)
#define EHCISTS_ASYNC_EMPTY BIT(13)
#define EHCISTS_HCHALTED BIT(12)
#define EHCISTS_ASYNC_ADV BIT( 5)
#define EHCISTS_HOST_ERR BIT( 4)
#define EHCISTS_FLIST_ROLL BIT( 3)
#define EHCISTS_PORTC_DET BIT( 2)
#define EHCISTS_USBERRINT BIT( 1)
#define EHCISTS_USBINT BIT( 0)
#define EHCISTS_MASK (EHCISTS_USBINT | EHCISTS_USBERRINT | \
EHCISTS_PORTC_DET | EHCISTS_FLIST_ROLL | \
EHCISTS_HOST_ERR | EHCISTS_ASYNC_ADV)
uint32_t usbsts; /* +0x04 */
#define EHCIINTR_ASYNC_ADV BIT( 5)
#define EHCIINTR_HOST_ERR BIT( 4)
#define EHCIINTR_FLIST_ROLL BIT( 3)
#define EHCIINTR_PORTC_DET BIT( 2)
#define EHCIINTR_USBERRINT BIT( 1)
#define EHCIINTR_USBINT BIT( 0)
uint32_t usbintr; /* +0x08 */
/// Translate a frame index into a micro frame index
#define FRAME2UFRAME(x) ((x) << 3)
/// Translate a micro frame index into a frame index
#define UFRAME2FRAME(x) ((x) >> 3)
#define FRINDEX_UF(x) ((x) & 0x7)
uint32_t frindex; /* +0x0C */
uint32_t ctrldssegment; /* +0x10 */
uint32_t periodiclistbase; /* +0x14 */
uint32_t asynclistaddr; /* +0x18 */
uint32_t res0[9];
#define EHCICFLAG_CFLAG BIT( 0)
uint32_t configflag; /* +0x40 */
#define EHCI_PORT_WO_OCURRENT BIT(22)
#define EHCI_PORT_WO_DCONNECT BIT(21)
#define EHCI_PORT_WO_CONNECT BIT(20)
#define EHCI_PORT_OWNER BIT(13)
#define EHCI_PORT_POWER BIT(12)
#define EHCI_PORT_JSTATE BIT(11)
#define EHCI_PORT_KSTATE BIT(10)
#define EHCI_PORT_SPEED_MASK (EHCI_PORT_JSTATE | EHCI_PORT_KSTATE)
#define EHCI_PORT_RESET BIT( 8)
#define EHCI_PORT_SUSPEND BIT( 7)
#define EHCI_PORT_FORCE_RESUME BIT( 6)
#define EHCI_PORT_OCURRENT_C BIT( 5)
#define EHCI_PORT_OCURRENT BIT( 4)
#define EHCI_PORT_ENABLE_C BIT( 3)
#define EHCI_PORT_ENABLE BIT( 2)
#define EHCI_PORT_CONNECT_C BIT( 1)
#define EHCI_PORT_CONNECT BIT( 0)
#define EHCI_PORT_CHANGE (EHCI_PORT_OCURRENT_C | \
EHCI_PORT_ENABLE_C | \
EHCI_PORT_CONNECT_C)
uint32_t portsc[]; /* +0x44 */
};
/*********************
**** Descriptors ****
*********************/
struct TD {
#define TDLP_INVALID BIT(0)
uint32_t next;
#define TDALTTDPTR_NAKCNT(x) (((x) >> 1) & 0x7)
uint32_t alt;
#define TDTOK_DT BIT(31)
#define TDTOK_BYTES(x) (((x) & 0x7fff) << 16)
#define TDTOK_BYTES_MASK TDTOK_BYTES(0x7fff)
#define TDTOK_GET_BYTES(x) (((x) & TDTOK_BYTES_MASK) >> 16)
#define TDTOK_IOC BIT(15)
#define TDTOK_C_PAGE(x) (((x) & 0x7) * BIT(12))
#define TDTOK_C_PAGE_MASK TDTOK_C_PAGE(0x7)
#define TDTOK_C_ERR(x) (((x) & 0x3) * BIT(10))
#define TDTOK_C_ERR_MASK TDTOK_C_ERR(0x3)
#define TDTOK_PID_OUT (0 * BIT(8))
#define TDTOK_PID_IN (1 * BIT(8))
#define TDTOK_PID_SETUP (2 * BIT(8))
#define TDTOK_SACTIVE BIT(7)
#define TDTOK_SHALTED BIT(6)
#define TDTOK_SBUFERR BIT(5)
#define TDTOK_SBABDET BIT(4)
#define TDTOK_SXACTERR BIT(3)
#define TDTOK_SUFRAME_MISS BIT(2)
#define TDTOK_SSPLITXSTATE BIT(1)
#define TDTOK_PINGSTATE BIT(0)
#define TDTOK_ERROR (TDTOK_SHALTED | \
TDTOK_SBUFERR | \
TDTOK_SBABDET | \
TDTOK_SXACTERR | \
TDTOK_SUFRAME_MISS)
uint32_t token;
#define TDBUF0_CUROFFSET(x) (((x) & 0xfff) * BIT(0))
#define TDBUF0_CUROFFSET_MASK TDBUF0_CUROFFSET(0xfff)
#define QHBUF1_CPROGMASK(x) (((x) & 0xff) * BIT(0))
#define QHBUF2_SBYTES(x) (((x) & 0xff) * BIT(5))
#define QHBUF2_FRAMETAG(x) (((x) & 0xf) * BIT(0))
uint32_t buf[5];
uint32_t buf_hi[5]; /* 64-bit capability(Appendix B) */
};
struct QH {
#define QHLP_TYPE_ITD (0x0 * BIT(1))
#define QHLP_TYPE_QH (0x1 * BIT(1))
#define QHLP_TYPE_SITD (0x2 * BIT(1))
#define QHLP_TYPE_FSTN (0x3 * BIT(1))
#define QHLP_INVALID BIT(0)
uint32_t qhlptr;
#define QHEPC0_NAKCNT_RL(x) (((x) & 0xf) * BIT(28))
#define QHEPC0_NAKCNT_RL_MASK QHEPC0_NAKCNT_RL(0xf)
#define QHEPC0_C BIT(27)
#define QHEPC0_MAXPKTLEN(x) (((x) & 0x7ff) * BIT(16))
#define QHEPC0_MAXPKT_MASK QHEPC0_MAXPKTLEN(0x7ff)
#define QHEPC0_GET_MAXPKT(x) (((x) & QHEPC0_MAXPKT_MASK) >> 16)
#define QHEPC0_H BIT(15)
#define QHEPC0_DTC BIT(14)
#define QHEPC0_FSPEED (0 * BIT(12))
#define QHEPC0_LSPEED (1 * BIT(12))
#define QHEPC0_HSPEED (2 * BIT(12))
#define QHEPC0_EP(x) (((x) & 0xf) * BIT( 8))
#define QHEPC0_I BIT(7)
#define QHEPC0_ADDR(x) (((x) & 0x7f) * BIT( 0))
#define QHEPC0_ADDR_MASK QHEPC0_ADDR(0x7f)
#define QHEPC0_GET_ADDR(x) ((x) & QHEPC0_ADDR_MASK)
#define QHEPC1_MULT(x) (((x) & 0x3) * BIT(30))
#define QHEPC1_PORT(x) (((x) & 0x7f) * BIT(23))
#define QHEPC1_HUB_ADDR(x) (((x) & 0x7f) * BIT(16))
#define QHEPC1_UFRAME_CMASK(x) (((x) & 0xff) * BIT( 8))
#define QHEPC1_UFRAME_SMASK(x) (((x) & 0xff) * BIT( 0))
#define QHEPC1_UFRAME_MASK (QHEPC1_UFRAME_CMASK(0xff) | \
QHEPC1_UFRAME_CMASK(0xff))
uint32_t epc[2];
uint32_t td_cur;
struct TD td_overlay;
};
/****************************
**** Private structures ****
****************************/
struct TDn {
volatile struct TD *td;
uintptr_t ptd;
usb_cb_t cb;
void *token;
struct TDn *next;
};
struct QHn {
/* Transaction data */
volatile struct QH *qh;
uintptr_t pqh;
struct TDn *tdns;
/* Interrupts */
int rate; //Polling frame rate(frame = 1ms, uframe = 125us)
int irq_pending;
int was_cancelled;
/* Links */
uint8_t owner_addr;
struct QHn *next;
/* Lock */
void *lock;
};
struct ehci_host {
int devid;
/* Hub emulation */
usb_hubem_t hubem;
void (*board_pwren) (int port, int state);
/* IRQ data */
struct xact irq_xact;
usb_cb_t irq_cb;
void *irq_token;
uint32_t bmreset_c;
/* Async schedule */
struct QHn *alist_tail;
struct QHn *db_pending;
struct QHn *db_active;
/* Periodic frame list */
uint32_t *flist;
uintptr_t pflist;
int flist_size;
struct QHn *intn_list;
struct QHn **shadow_flist;
/* Standard registers */
volatile struct ehci_host_cap *cap_regs;
volatile struct ehci_host_op *op_regs;
/* Support */
ps_dma_man_t *dman;
ps_mutex_ops_t *sync;
void *state;
};
/**
* Hub Emulation
*/
volatile uint32_t *_get_portsc(struct ehci_host *h, int port);
int _set_pf(void *token, int port, enum port_feature feature);
int _clr_pf(void *token, int port, enum port_feature feature);
int _get_pstat(void *token, int port, struct port_status *_ps);
/**
* Async Scheduling
*/
void ehci_handle_irq(usb_host_t * hdev);
int ehci_cancel_xact(usb_host_t * hdev, struct endpoint *ep);
void qhn_destroy(ps_dma_man_t * dman, struct QHn *qhn);
int ehci_wait_for_completion(struct TDn *tdn);
void ehci_schedule_async(struct ehci_host *edev, struct QHn *qh_new);
enum usb_xact_status qtd_get_status(volatile struct TD *qtd);
enum usb_xact_status qhn_get_status(struct QHn *qhn);
void check_doorbell(struct ehci_host *edev);
/* New APIs */
struct QHn *qhn_alloc(struct ehci_host *edev, uint8_t address, uint8_t hub_addr,
uint8_t hub_port, enum usb_speed speed,
struct endpoint *ep);
struct TDn *qtd_alloc(struct ehci_host *edev, enum usb_speed speed,
struct endpoint *ep, struct xact *xact, int nxact,
usb_cb_t cb, void *token);
void qhn_update(struct QHn *qhn, uint8_t address, struct endpoint *ep);
void qtd_enqueue(struct ehci_host *edev, struct QHn *qhn, struct TDn *tdn);
void ehci_add_qhn_async(struct ehci_host *edev, struct QHn *qhn);
void ehci_add_qhn_periodic(struct ehci_host *edev, struct QHn *qhn);
void ehci_del_qhn_async(struct ehci_host *edev, struct QHn *qhn);
void ehci_del_qhn_periodic(struct ehci_host *edev, struct QHn *qhn);
void ehci_async_complete(struct ehci_host *edev);
/**
* Periodic Scheduling
*/
int ehci_schedule_periodic_root(struct ehci_host *edev, struct xact *xact,
int nxact, usb_cb_t cb, void *t);
int ehci_schedule_periodic(struct ehci_host *edev);
void ehci_periodic_complete(struct ehci_host *edev);
enum usb_xact_status qhn_wait(struct QHn *qhn, int to_ms);
/**
* Debugging
*/
void dump_qtd(volatile struct TD *qtd);
void dump_qhn(struct QHn *qhn);
void dump_q(struct QHn *qhn);
void dump_edev(struct ehci_host *edev);
/**
* Initialise a EHCI host controller
* @param[in/out] hdev A host controller structure to
* populate. Must be pre-filled with a
* DMA allocator. This function will
* fill the private data and function
* pointers of this structure.
* @param[in] cap_regs memory location of the mapped ECHI
* capability registers
* @param[int] board_pwren Function to call when power on/off
* a port. Generally the PHY will take
* care of this, but in cases where there
* is no PHY (HSIC), a GPIO, etc may need
* to be manually controlled.
* @return 0 on success
*/
int ehci_host_init(usb_host_t * hdev, uintptr_t cap_regs,
void (*board_pwren) (int port, int state));
#endif /* _EHCI_EHCI_H_ */