dect
/
linux-2.6
Archived
13
0
Fork 0

usb: pch_udc: Fixed issue which does not work with g_ether

This PCH_UDC driver does not work normally when "Ethernet gadget" is used.

This patch fixed this issue.
The following was modified.
 - The FIFO flush process.
 - The descriptor creation process.
 - The adjustment of DMA buffer align.

Currently the PCH_UDC driver can work normally with "Ethernet gadget",
"Serial gadget" or "File-backed Storage Gadget".

Signed-off-by: Toshiharu Okada <toshiharu-linux@dsn.okisemi.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
This commit is contained in:
Toshiharu Okada 2011-02-07 17:01:26 +09:00 committed by Greg Kroah-Hartman
parent 4f22ce7045
commit c17f459c6e
1 changed files with 104 additions and 74 deletions

View File

@ -367,7 +367,6 @@ struct pch_udc_dev {
static const char ep0_string[] = "ep0in";
static DEFINE_SPINLOCK(udc_stall_spinlock); /* stall spin lock */
struct pch_udc_dev *pch_udc; /* pointer to device object */
static int speed_fs;
module_param_named(speed_fs, speed_fs, bool, S_IRUGO);
MODULE_PARM_DESC(speed_fs, "true for Full speed operation");
@ -383,6 +382,8 @@ MODULE_PARM_DESC(speed_fs, "true for Full speed operation");
* @dma_mapped: DMA memory mapped for request
* @dma_done: DMA completed for request
* @chain_len: chain length
* @buf: Buffer memory for align adjustment
* @dma: DMA memory for align adjustment
*/
struct pch_udc_request {
struct usb_request req;
@ -394,6 +395,8 @@ struct pch_udc_request {
dma_mapped:1,
dma_done:1;
unsigned chain_len;
void *buf;
dma_addr_t dma;
};
static inline u32 pch_udc_readl(struct pch_udc_dev *dev, unsigned long reg)
@ -615,7 +618,7 @@ static inline void pch_udc_ep_set_trfr_type(struct pch_udc_ep *ep,
/**
* pch_udc_ep_set_bufsz() - Set the maximum packet size for the endpoint
* @ep: Reference to structure of type pch_udc_ep_regs
* @buf_size: The buffer size
* @buf_size: The buffer word size
*/
static void pch_udc_ep_set_bufsz(struct pch_udc_ep *ep,
u32 buf_size, u32 ep_in)
@ -635,7 +638,7 @@ static void pch_udc_ep_set_bufsz(struct pch_udc_ep *ep,
/**
* pch_udc_ep_set_maxpkt() - Set the Max packet size for the endpoint
* @ep: Reference to structure of type pch_udc_ep_regs
* @pkt_size: The packet size
* @pkt_size: The packet byte size
*/
static void pch_udc_ep_set_maxpkt(struct pch_udc_ep *ep, u32 pkt_size)
{
@ -920,25 +923,10 @@ static void pch_udc_ep_clear_nak(struct pch_udc_ep *ep)
*/
static void pch_udc_ep_fifo_flush(struct pch_udc_ep *ep, int dir)
{
unsigned int loopcnt = 0;
struct pch_udc_dev *dev = ep->dev;
if (dir) { /* IN ep */
pch_udc_ep_bit_set(ep, UDC_EPCTL_ADDR, UDC_EPCTL_F);
return;
}
if (pch_udc_read_ep_status(ep) & UDC_EPSTS_MRXFIFO_EMP)
return;
pch_udc_ep_bit_set(ep, UDC_EPCTL_ADDR, UDC_EPCTL_MRXFLUSH);
/* Wait for RxFIFO Empty */
loopcnt = 10000;
while (!(pch_udc_read_ep_status(ep) & UDC_EPSTS_MRXFIFO_EMP) &&
--loopcnt)
udelay(5);
if (!loopcnt)
dev_err(&dev->pdev->dev, "RxFIFO not Empty\n");
pch_udc_ep_bit_clr(ep, UDC_EPCTL_ADDR, UDC_EPCTL_MRXFLUSH);
}
/**
@ -1220,14 +1208,31 @@ static void complete_req(struct pch_udc_ep *ep, struct pch_udc_request *req,
dev = ep->dev;
if (req->dma_mapped) {
if (ep->in)
dma_unmap_single(&dev->pdev->dev, req->req.dma,
req->req.length, DMA_TO_DEVICE);
else
dma_unmap_single(&dev->pdev->dev, req->req.dma,
req->req.length, DMA_FROM_DEVICE);
if (req->dma == DMA_ADDR_INVALID) {
if (ep->in)
dma_unmap_single(&dev->pdev->dev, req->req.dma,
req->req.length,
DMA_TO_DEVICE);
else
dma_unmap_single(&dev->pdev->dev, req->req.dma,
req->req.length,
DMA_FROM_DEVICE);
req->req.dma = DMA_ADDR_INVALID;
} else {
if (ep->in)
dma_unmap_single(&dev->pdev->dev, req->dma,
req->req.length,
DMA_TO_DEVICE);
else {
dma_unmap_single(&dev->pdev->dev, req->dma,
req->req.length,
DMA_FROM_DEVICE);
memcpy(req->req.buf, req->buf, req->req.length);
}
kfree(req->buf);
req->dma = DMA_ADDR_INVALID;
}
req->dma_mapped = 0;
req->req.dma = DMA_ADDR_INVALID;
}
ep->halted = 1;
spin_unlock(&dev->lock);
@ -1268,12 +1273,18 @@ static void pch_udc_free_dma_chain(struct pch_udc_dev *dev,
struct pch_udc_data_dma_desc *td = req->td_data;
unsigned i = req->chain_len;
dma_addr_t addr2;
dma_addr_t addr = (dma_addr_t)td->next;
td->next = 0x00;
for (; i > 1; --i) {
dma_addr_t addr = (dma_addr_t)td->next;
/* do not free first desc., will be done by free for request */
td = phys_to_virt(addr);
addr2 = (dma_addr_t)td->next;
pci_pool_free(dev->data_requests, td, addr);
td->next = 0x00;
addr = addr2;
}
req->chain_len = 1;
}
/**
@ -1301,23 +1312,23 @@ static int pch_udc_create_dma_chain(struct pch_udc_ep *ep,
if (req->chain_len > 1)
pch_udc_free_dma_chain(ep->dev, req);
for (; ; bytes -= buf_len, ++len) {
if (ep->in)
td->status = PCH_UDC_BS_HST_BSY | min(buf_len, bytes);
else
td->status = PCH_UDC_BS_HST_BSY;
if (req->dma == DMA_ADDR_INVALID)
td->dataptr = req->req.dma;
else
td->dataptr = req->dma;
td->status = PCH_UDC_BS_HST_BSY;
for (; ; bytes -= buf_len, ++len) {
td->status = PCH_UDC_BS_HST_BSY | min(buf_len, bytes);
if (bytes <= buf_len)
break;
last = td;
td = pci_pool_alloc(ep->dev->data_requests, gfp_flags,
&dma_addr);
if (!td)
goto nomem;
i += buf_len;
td->dataptr = req->req.dma + i;
td->dataptr = req->td_data->dataptr + i;
last->next = dma_addr;
}
@ -1352,28 +1363,15 @@ static int prepare_dma(struct pch_udc_ep *ep, struct pch_udc_request *req,
{
int retval;
req->td_data->dataptr = req->req.dma;
req->td_data->status |= PCH_UDC_DMA_LAST;
/* Allocate and create a DMA chain */
retval = pch_udc_create_dma_chain(ep, req, ep->ep.maxpacket, gfp);
if (retval) {
pr_err("%s: could not create DMA chain: %d\n",
__func__, retval);
pr_err("%s: could not create DMA chain:%d\n", __func__, retval);
return retval;
}
if (!ep->in)
return 0;
if (req->req.length <= ep->ep.maxpacket)
req->td_data->status = PCH_UDC_DMA_LAST | PCH_UDC_BS_HST_BSY |
req->req.length;
/* if bytes < max packet then tx bytes must
* be written in packet per buffer mode
*/
if ((req->req.length < ep->ep.maxpacket) || !ep->num)
if (ep->in)
req->td_data->status = (req->td_data->status &
~PCH_UDC_RXTX_BYTES) | req->req.length;
req->td_data->status = (req->td_data->status &
~PCH_UDC_BUFF_STS) | PCH_UDC_BS_HST_BSY;
~PCH_UDC_BUFF_STS) | PCH_UDC_BS_HST_RDY;
return 0;
}
@ -1529,6 +1527,7 @@ static struct usb_request *pch_udc_alloc_request(struct usb_ep *usbep,
if (!req)
return NULL;
req->req.dma = DMA_ADDR_INVALID;
req->dma = DMA_ADDR_INVALID;
INIT_LIST_HEAD(&req->queue);
if (!ep->dev->dma_addr)
return &req->req;
@ -1613,16 +1612,33 @@ static int pch_udc_pcd_queue(struct usb_ep *usbep, struct usb_request *usbreq,
/* map the buffer for dma */
if (usbreq->length &&
((usbreq->dma == DMA_ADDR_INVALID) || !usbreq->dma)) {
if (ep->in)
usbreq->dma = dma_map_single(&dev->pdev->dev,
usbreq->buf,
usbreq->length,
DMA_TO_DEVICE);
else
usbreq->dma = dma_map_single(&dev->pdev->dev,
usbreq->buf,
usbreq->length,
DMA_FROM_DEVICE);
if (!((unsigned long)(usbreq->buf) & 0x03)) {
if (ep->in)
usbreq->dma = dma_map_single(&dev->pdev->dev,
usbreq->buf,
usbreq->length,
DMA_TO_DEVICE);
else
usbreq->dma = dma_map_single(&dev->pdev->dev,
usbreq->buf,
usbreq->length,
DMA_FROM_DEVICE);
} else {
req->buf = kzalloc(usbreq->length, GFP_ATOMIC);
if (!req->buf)
return -ENOMEM;
if (ep->in) {
memcpy(req->buf, usbreq->buf, usbreq->length);
req->dma = dma_map_single(&dev->pdev->dev,
req->buf,
usbreq->length,
DMA_TO_DEVICE);
} else
req->dma = dma_map_single(&dev->pdev->dev,
req->buf,
usbreq->length,
DMA_FROM_DEVICE);
}
req->dma_mapped = 1;
}
if (usbreq->length > 0) {
@ -1920,32 +1936,46 @@ static void pch_udc_complete_receiver(struct pch_udc_ep *ep)
struct pch_udc_request *req;
struct pch_udc_dev *dev = ep->dev;
unsigned int count;
struct pch_udc_data_dma_desc *td;
dma_addr_t addr;
if (list_empty(&ep->queue))
return;
/* next request */
req = list_entry(ep->queue.next, struct pch_udc_request, queue);
if ((req->td_data_last->status & PCH_UDC_BUFF_STS) !=
PCH_UDC_BS_DMA_DONE)
return;
pch_udc_clear_dma(ep->dev, DMA_DIR_RX);
pch_udc_ep_set_ddptr(ep, 0);
if ((req->td_data_last->status & PCH_UDC_RXTX_STS) !=
PCH_UDC_RTS_SUCC) {
dev_err(&dev->pdev->dev, "Invalid RXTX status (0x%08x) "
"epstatus=0x%08x\n",
(req->td_data_last->status & PCH_UDC_RXTX_STS),
(int)(ep->epsts));
return;
}
count = req->td_data_last->status & PCH_UDC_RXTX_BYTES;
if ((req->td_data_last->status & PCH_UDC_BUFF_STS) ==
PCH_UDC_BS_DMA_DONE)
td = req->td_data_last;
else
td = req->td_data;
while (1) {
if ((td->status & PCH_UDC_RXTX_STS) != PCH_UDC_RTS_SUCC) {
dev_err(&dev->pdev->dev, "Invalid RXTX status=0x%08x "
"epstatus=0x%08x\n",
(req->td_data->status & PCH_UDC_RXTX_STS),
(int)(ep->epsts));
return;
}
if ((td->status & PCH_UDC_BUFF_STS) == PCH_UDC_BS_DMA_DONE)
if (td->status | PCH_UDC_DMA_LAST) {
count = td->status & PCH_UDC_RXTX_BYTES;
break;
}
if (td == req->td_data_last) {
dev_err(&dev->pdev->dev, "Not complete RX descriptor");
return;
}
addr = (dma_addr_t)td->next;
td = phys_to_virt(addr);
}
/* on 64k packets the RXBYTES field is zero */
if (!count && (req->req.length == UDC_DMA_MAXPACKET))
count = UDC_DMA_MAXPACKET;
req->td_data->status |= PCH_UDC_DMA_LAST;
req->td_data_last->status |= PCH_UDC_BS_HST_BSY;
td->status |= PCH_UDC_BS_HST_BSY;
req->dma_going = 0;
req->req.actual = count;