Commit 07e42948 authored by 薛德章's avatar 薛德章

USB CPPI DMA patch for davinci

This patch fixed USB unstability issue while writing
huge amount of data into usb device.
parent c907be9c
This diff is collapsed.
......@@ -11,14 +11,21 @@
#include "musb_dma.h"
#include "musb_core.h"
#include "davinci.h"
/* hOptions bit masks for CPPI BDs */
#define CPPI_SOP_SET ((u32)(1 << 31))
#define CPPI_EOP_SET ((u32)(1 << 30))
#define CPPI_OWN_SET ((u32)(1 << 29)) /* owned by cppi */
#define CPPI_EOQ_MASK ((u32)(1 << 28))
#define CPPI_ZERO_SET ((u32)(1 << 23)) /* rx saw zlp; tx issues one */
#define CPPI_RXABT_MASK ((u32)(1 << 19)) /* need more rx buffers */
/* FIXME fully isolate CPPI from DaVinci ... the "CPPI generic" registers
* would seem to be shared with the TUSB6020 (over VLYNQ).
*/
#include "davinci.h"
#define CPPI_RECV_PKTLEN_MASK 0xFFFF
#define CPPI_BUFFER_LEN_MASK 0xFFFF
#define CPPI_TEAR_READY ((u32)(1 << 31))
#define CPPI_CHNUM_BITS_MASK 0x3
/* CPPI RX/TX state RAM */
......@@ -44,64 +51,77 @@ struct cppi_rx_stateram {
u32 rx_complete;
};
/* hw_options bits in CPPI buffer descriptors */
#define CPPI_SOP_SET ((u32)(1 << 31))
#define CPPI_EOP_SET ((u32)(1 << 30))
#define CPPI_OWN_SET ((u32)(1 << 29)) /* owned by cppi */
#define CPPI_EOQ_MASK ((u32)(1 << 28))
#define CPPI_ZERO_SET ((u32)(1 << 23)) /* rx saw zlp; tx issues one */
#define CPPI_RXABT_MASK ((u32)(1 << 19)) /* need more rx buffers */
#define CPPI_RECV_PKTLEN_MASK 0xFFFF
#define CPPI_BUFFER_LEN_MASK 0xFFFF
#define CPPI_TEAR_READY ((u32)(1 << 31))
/* CPPI data structure definitions */
#define CPPI_DESCRIPTOR_ALIGN 16 /* bytes; 5-dec docs say 4-byte align */
/**
* CPPI Buffer Descriptor
*
* Buffer Descriptor structure for USB OTG Module CPPI.Using the same across
* Tx/Rx
*/
struct cppi_descriptor {
/* hardware overlay */
u32 hw_next; /* next buffer descriptor Pointer */
u32 hw_bufp; /* i/o buffer pointer */
u32 hw_off_len; /* buffer_offset16, buffer_length16 */
u32 hw_options; /* flags: SOP, EOP etc*/
#define CPPI_DESCRIPTOR_ALIGN 16 /* bytes; 5-dec docs say 4-byte align*/
struct cppi_descriptor *next;
struct cppi_descriptor {
/* Hardware Overlay */
u32 hw_next; /**< Next(hardware) Buffer Descriptor Pointer */
u32 hw_bufp; /**<Buffer Pointer (dma_addr_t) */
u32 hw_off_len; /**<Buffer_offset16,buffer_length16 */
u32 hw_options; /**<Option fields for SOP,EOP etc*/
struct cppi_descriptor *next; /**<Next(software) Buffer Descriptor
*pointer
*/
dma_addr_t dma; /* address of this descriptor */
u32 buflen; /* for RX: original buffer length */
/* for Rx Desc, keep track of enqueued Buffer len to detect
* short packets
*/
u32 buflen;
} __attribute__ ((aligned(CPPI_DESCRIPTOR_ALIGN)));
/* forward declaration for CppiDmaController structure */
struct cppi;
/* CPPI Channel Control structure */
/**
* Channel Control Structure
*
* CPPI Channel Control structure. Using he same for Tx/Rx. If need be
* derive out of this later.
*/
struct cppi_channel {
/* First field must be dma_channel for easy type casting
* FIXME just use container_of() and be typesafe instead!
*/
struct dma_channel channel;
/* back pointer to the DMA controller structure */
/* back pointer to the Dma Controller structure */
struct cppi *controller;
/* which direction of which endpoint? */
struct musb_hw_ep *hw_ep;
bool transmit;
u8 transmit;
u8 index;
/* DMA modes: RNDIS or "transparent" */
u8 is_rndis;
u8 autoreq; /* For now keep this remove this
* one RNDIS + length <64 segmenstation
* will done
*/
/* Rx Requested mode */
u8 rxmode;
u8 reqcomplete; /* zero packet handling*/
/* book keeping for current transfer request */
dma_addr_t buf_dma;
u32 buf_len;
u32 maxpacket;
u32 offset; /* dma requested */
u32 offset; /* requested segments */
u32 actuallen; /* completed (Channel.actual) */
void __iomem *state_ram; /* CPPI state */
struct cppi_descriptor *freelist;
/* BD management fields */
struct cppi_descriptor *freelist; /* Free BD Pool head pointer */
struct cppi_descriptor *head;
struct cppi_descriptor *tail;
struct cppi_descriptor *last_processed;
......@@ -110,14 +130,24 @@ struct cppi_channel {
* FIFONOTEMPTY to clear.
*/
struct list_head tx_complete;
u32 iso_desc;
};
/* CPPI DMA controller object */
/**
* CPPI Dma Controller Object
*
* CPPI Dma controller object.Encapsulates all bookeeping and Data
* structures pertaining to the CPPI Dma Controller.
*/
struct cppi {
/* FIXME switchover to container_of() and remove the
* unsafe typecasts...
*/
struct dma_controller controller;
struct musb *musb;
void __iomem *mregs; /* Mentor regs */
void __iomem *tibase; /* TI/CPPI regs */
void __iomem *mregs;
void __iomem *tibase;
struct cppi_channel tx[MUSB_C_NUM_EPT - 1];
struct cppi_channel rx[MUSB_C_NUM_EPR - 1];
......
......@@ -2028,6 +2028,11 @@ musb_init_controller(struct device *dev, int nIrq, void __iomem *ctrl)
musb->xceiv.host = &hcd->self;
hcd->power_budget = 2 * (plat->power ? : 250);
}
#ifdef CONFIG_ARCH_DAVINCI
tasklet_init (&musb->fifo_check, musb_fifo_check_tasklet,
(unsigned long)musb);
#endif
#endif /* CONFIG_USB_MUSB_HDRC_HCD */
/* For the host-only role, we can activate right away.
......
......@@ -239,6 +239,12 @@ enum musb_g_ep0_state {
(musb_readb((_x)->mregs, MUSB_DEVCTL)&MUSB_DEVCTL_HM)
#define MUSB_MODE(musb) ((musb)->is_host ? "Host" : "Peripheral")
struct musb_iso_desc {
u32 offset;
u32 length;
u32 status;
};
/******************************** TYPES *************************************/
......@@ -285,6 +291,9 @@ struct musb_hw_ep {
u8 rx_reinit;
u8 tx_reinit;
#ifdef CONFIG_ARCH_DAVINCI
u8 fifo_flush_check; /* Check FIFO empty */
#endif
#endif
#ifdef CONFIG_USB_GADGET_MUSB_HDRC
......@@ -292,6 +301,9 @@ struct musb_hw_ep {
struct musb_ep ep_in; /* TX */
struct musb_ep ep_out; /* RX */
#endif
struct musb_iso_desc *iso_desc;
u32 num_iso_desc;
u8 zero;
};
static inline struct usb_request *next_in_request(struct musb_hw_ep *hw_ep)
......@@ -343,6 +355,11 @@ struct musb {
struct list_head in_bulk; /* of musb_qh */
struct list_head out_bulk; /* of musb_qh */
struct musb_qh *periodic[32]; /* tree of interrupt+iso */
#ifdef CONFIG_ARCH_DAVINCI
struct tasklet_struct fifo_check; /* tasklet for FIFO empty
* status check
*/
#endif
#endif
/* called with IRQs blocked; ON/nonzero implies starting a session,
......
......@@ -154,7 +154,8 @@ static inline void cppi_host_txdma_start(struct musb_hw_ep *ep)
/* NOTE: no locks here; caller should lock and select EP */
txcsr = musb_readw(ep->regs, MUSB_TXCSR);
txcsr |= MUSB_TXCSR_DMAENAB | MUSB_TXCSR_H_WZC_BITS;
txcsr |= MUSB_TXCSR_DMAENAB | MUSB_TXCSR_DMAMODE |
MUSB_TXCSR_H_WZC_BITS;
musb_writew(ep->regs, MUSB_TXCSR, txcsr);
}
......@@ -1242,6 +1243,9 @@ void musb_host_tx(struct musb *musb, u8 epnum)
/* REVISIT may need to clear FLUSHFIFO ... */
musb_writew(epio, MUSB_TXCSR, tx_csr);
musb_writeb(epio, MUSB_TXINTERVAL, 0);
#ifdef CONFIG_ARCH_DAVINCI
hw_ep->fifo_flush_check = 0;
#endif
done = true;
}
......@@ -1305,8 +1309,19 @@ void musb_host_tx(struct musb *musb, u8 epnum)
/* set status */
urb->status = status;
urb->actual_length = qh->offset;
#ifdef CONFIG_ARCH_DAVINCI
/* Check for FIFO empty status. If not wait for the same
* before completing the urb. This ensures that the toggle
* status is correctly preserved and data will not be lost.
*/
if ((tx_csr & MUSB_TXCSR_FIFONOTEMPTY) ||
(tx_csr & MUSB_TXCSR_TXPKTRDY)) {
hw_ep->fifo_flush_check = 1;
tasklet_schedule(&musb->fifo_check);
goto finish;
}
#endif
musb_advance_schedule(musb, urb, hw_ep, USB_DIR_OUT);
} else if (!(tx_csr & MUSB_TXCSR_DMAENAB)) {
/* WARN_ON(!buf); */
......@@ -1944,6 +1959,9 @@ static int musb_cleanup_urb(struct urb *urb, struct musb_qh *qh, int is_in)
musb_writew(epio, MUSB_TXCSR, csr);
/* flush cpu writebuffer */
csr = musb_readw(epio, MUSB_TXCSR);
#ifdef CONFIG_ARCH_DAVINCI
ep->fifo_flush_check = 0;
#endif
}
if (status == 0)
musb_advance_schedule(ep->musb, urb, ep, is_in);
......@@ -2139,6 +2157,46 @@ static int musb_bus_suspend(struct usb_hcd *hcd)
return 0;
}
#ifdef CONFIG_ARCH_DAVINCI
/* Tasklet routine to handle the completion request. Check for Fifo status
* before completing the request. Avoids false completions when data is still
* in the fifo
*/
void musb_fifo_check_tasklet(unsigned long data)
{
struct musb *musb = (struct musb *)data;
u8 epnum = 1, sch_tsklt = 0;
struct musb_hw_ep *hw_ep = NULL;
unsigned long flags;
u16 csr;
struct musb_qh *qh;
do {
hw_ep = &(musb->endpoints[epnum++]);
spin_lock_irqsave(&musb->lock, flags);
if (hw_ep->fifo_flush_check) {
csr = musb_readw(hw_ep->regs, MUSB_TXCSR);
if (((csr & MUSB_TXCSR_FIFONOTEMPTY) ||
(csr & MUSB_TXCSR_TXPKTRDY)))
sch_tsklt = 1;
else {
hw_ep->fifo_flush_check = 0;
qh = hw_ep->out_qh;
musb_advance_schedule(musb, next_urb(qh),
hw_ep, USB_DIR_OUT);
DBG(6, "Completed Tasklet %d\n", hw_ep->epnum);
}
}
spin_unlock_irqrestore(&musb->lock, flags);
} while (epnum < MUSB_C_NUM_EPS);
if (sch_tsklt)
tasklet_schedule(&musb->fifo_check);
}
#endif
static int musb_bus_resume(struct usb_hcd *hcd)
{
/* resuming child port does the work */
......
......@@ -88,7 +88,9 @@ extern int musb_hub_status_data(struct usb_hcd *hcd, char *buf);
extern int musb_hub_control(struct usb_hcd *hcd,
u16 typeReq, u16 wValue, u16 wIndex,
char *buf, u16 wLength);
#ifdef CONFIG_ARCH_DAVINCI
extern void musb_fifo_check_tasklet (unsigned long data);
#endif
extern const struct hc_driver musb_hc_driver;
static inline struct urb *next_urb(struct musb_qh *qh)
......@@ -108,3 +110,4 @@ static inline struct urb *next_urb(struct musb_qh *qh)
}
#endif /* _MUSB_HOST_H */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment