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,120 +11,150 @@
#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 */
struct cppi_tx_stateram {
u32 tx_head; /* "DMA packet" head descriptor */
u32 tx_head; /* "DMA packet" head descriptor */
u32 tx_buf;
u32 tx_current; /* current descriptor */
u32 tx_current; /* current descriptor */
u32 tx_buf_current;
u32 tx_info; /* flags, remaining buflen */
u32 tx_info; /* flags, remaining buflen */
u32 tx_rem_len;
u32 tx_dummy; /* unused */
u32 tx_dummy; /* unused */
u32 tx_complete;
};
struct cppi_rx_stateram {
u32 rx_skipbytes;
u32 rx_head;
u32 rx_sop; /* "DMA packet" head descriptor */
u32 rx_current; /* current descriptor */
u32 rx_sop; /* "DMA packet" head descriptor */
u32 rx_current; /* current descriptor */
u32 rx_buf_current;
u32 rx_len_len;
u32 rx_cnt_cnt;
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
*/
#define CPPI_DESCRIPTOR_ALIGN 16 /* bytes; 5-dec docs say 4-byte align*/
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*/
struct cppi_descriptor *next;
dma_addr_t dma; /* address of this descriptor */
u32 buflen; /* for RX: original buffer length */
/* 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 */
/* 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 {
struct dma_channel 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 */
struct cppi *controller;
/* back pointer to the Dma Controller structure */
struct cppi *controller;
/* which direction of which endpoint? */
struct musb_hw_ep *hw_ep;
bool transmit;
u8 index;
struct musb_hw_ep *hw_ep;
u8 transmit;
u8 index;
/* DMA modes: RNDIS or "transparent" */
u8 is_rndis;
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 */
dma_addr_t buf_dma;
u32 buf_len;
u32 maxpacket;
u32 offset; /* requested segments */
u32 actuallen; /* completed (Channel.actual) */
void __iomem *state_ram; /* CPPI state */
struct cppi_descriptor *freelist;
void __iomem *state_ram; /* CPPI state */
/* BD management fields */
struct cppi_descriptor *head;
struct cppi_descriptor *tail;
struct cppi_descriptor *last_processed;
struct cppi_descriptor *freelist; /* Free BD Pool head pointer */
struct cppi_descriptor *head;
struct cppi_descriptor *tail;
struct cppi_descriptor *last_processed;
/* use tx_complete in host role to track endpoints waiting for
* FIFONOTEMPTY to clear.
*/
struct list_head tx_complete;
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 {
struct dma_controller controller;
struct musb *musb;
void __iomem *mregs; /* Mentor regs */
void __iomem *tibase; /* TI/CPPI regs */
/* FIXME switchover to container_of() and remove the
* unsafe typecasts...
*/
struct dma_controller controller;
struct musb *musb;
void __iomem *mregs;
void __iomem *tibase;
struct cppi_channel tx[MUSB_C_NUM_EPT - 1];
struct cppi_channel rx[MUSB_C_NUM_EPR - 1];
struct cppi_channel tx[MUSB_C_NUM_EPT - 1];
struct cppi_channel rx[MUSB_C_NUM_EPR - 1];
struct dma_pool *pool;
struct dma_pool *pool;
struct list_head tx_complete;
struct list_head tx_complete;
};
/* irq handling hook */
......
......@@ -60,23 +60,23 @@
* includes DaVinci EVM in a common non-OTG mode.
*
* * Control and bulk use dedicated endpoints, and there's as
* yet no mechanism to either (a) reclaim the hardware when
* peripherals are NAKing, which gets complicated with bulk
* endpoints, or (b) use more than a single bulk endpoint in
* each direction.
* yet no mechanism to either (a) reclaim the hardware when
* peripherals are NAKing, which gets complicated with bulk
* endpoints, or (b) use more than a single bulk endpoint in
* each direction.
*
* RESULT: one device may be perceived as blocking another one.
* RESULT: one device may be perceived as blocking another one.
*
* * Interrupt and isochronous will dynamically allocate endpoint
* hardware, but (a) there's no record keeping for bandwidth;
* (b) in the common case that few endpoints are available, there
* is no mechanism to reuse endpoints to talk to multiple devices.
* hardware, but (a) there's no record keeping for bandwidth;
* (b) in the common case that few endpoints are available, there
* is no mechanism to reuse endpoints to talk to multiple devices.
*
* RESULT: At one extreme, bandwidth can be overcommitted in
* some hardware configurations, no faults will be reported.
* At the other extreme, the bandwidth capabilities which do
* exist tend to be severely undercommitted. You can't yet hook
* up both a keyboard and a mouse to an external USB hub.
* RESULT: At one extreme, bandwidth can be overcommitted in
* some hardware configurations, no faults will be reported.
* At the other extreme, the bandwidth capabilities which do
* exist tend to be severely undercommitted. You can't yet hook
* up both a keyboard and a mouse to an external USB hub.
*/
/*
......@@ -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); */
......@@ -1358,9 +1373,9 @@ finish:
* (a) all URBs terminate with REQPKT cleared and fifo(s) empty;
* (b) termination conditions are: short RX, or buffer full;
* (c) fault modes include
* - iff URB_SHORT_NOT_OK, short RX status is -EREMOTEIO.
* (and that endpoint's dma queue stops immediately)
* - overflow (full, PLUS more bytes in the terminal packet)
* - iff URB_SHORT_NOT_OK, short RX status is -EREMOTEIO.
* (and that endpoint's dma queue stops immediately)
* - overflow (full, PLUS more bytes in the terminal packet)
*
* So for example, usb-storage sets URB_SHORT_NOT_OK, and would
* thus be a great candidate for using mode 1 ... for all but the
......@@ -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