diff options
Diffstat (limited to 'drivers/staging/octeon-usb/octeon-hcd.c')
-rw-r--r-- | drivers/staging/octeon-usb/octeon-hcd.c | 130 |
1 files changed, 90 insertions, 40 deletions
diff --git a/drivers/staging/octeon-usb/octeon-hcd.c b/drivers/staging/octeon-usb/octeon-hcd.c index 8b8ce7293c52..c4c731f60529 100644 --- a/drivers/staging/octeon-usb/octeon-hcd.c +++ b/drivers/staging/octeon-usb/octeon-hcd.c @@ -47,6 +47,7 @@ #include <linux/module.h> #include <linux/init.h> #include <linux/pci.h> +#include <linux/prefetch.h> #include <linux/interrupt.h> #include <linux/platform_device.h> #include <linux/usb.h> @@ -245,9 +246,6 @@ enum cvmx_usb_pipe_flags { __CVMX_USB_PIPE_FLAGS_NEED_PING = 1 << 18, }; -/* Normal prefetch that use the pref instruction. */ -#define CVMX_PREFETCH(address, offset) asm volatile ("pref %[type], %[off](%[rbase])" : : [rbase] "d" (address), [off] "I" (offset), [type] "n" (0)) - /* Maximum number of times to retry failed transactions */ #define MAX_RETRIES 3 @@ -462,7 +460,8 @@ struct octeon_hcd { } while (0) /* Returns the IO address to push/pop stuff data from the FIFOs */ -#define USB_FIFO_ADDRESS(channel, usb_index) (CVMX_USBCX_GOTGCTL(usb_index) + ((channel)+1)*0x1000) +#define USB_FIFO_ADDRESS(channel, usb_index) \ + (CVMX_USBCX_GOTGCTL(usb_index) + ((channel)+1)*0x1000) /** * struct octeon_temp_buffer - a bounce buffer for USB transfers @@ -892,6 +891,7 @@ static int cvmx_usb_initialize(struct cvmx_usb_state *usb, */ { union cvmx_usbcx_gusbcfg usbcx_gusbcfg; + usbcx_gusbcfg.u32 = __cvmx_usb_read_csr32(usb, CVMX_USBCX_GUSBCFG(usb->index)); usbcx_gusbcfg.s.toutcal = 0; @@ -949,6 +949,7 @@ static int cvmx_usb_initialize(struct cvmx_usb_state *usb, */ { union cvmx_usbcx_hcfg usbcx_hcfg; + usbcx_hcfg.u32 = __cvmx_usb_read_csr32(usb, CVMX_USBCX_HCFG(usb->index)); usbcx_hcfg.s.fslssupp = 0; @@ -1076,6 +1077,7 @@ static int cvmx_usb_enable(struct cvmx_usb_state *usb) */ { union cvmx_usbcx_gnptxfsiz siz; + siz.u32 = __cvmx_usb_read_csr32(usb, CVMX_USBCX_GNPTXFSIZ(usb->index)); siz.s.nptxfdep = usbcx_ghwcfg3.s.dfifodepth / 2; @@ -1090,6 +1092,7 @@ static int cvmx_usb_enable(struct cvmx_usb_state *usb) */ { union cvmx_usbcx_hptxfsiz siz; + siz.u32 = __cvmx_usb_read_csr32(usb, CVMX_USBCX_HPTXFSIZ(usb->index)); siz.s.ptxfsize = usbcx_ghwcfg3.s.dfifodepth / 4; @@ -1219,8 +1222,8 @@ static struct cvmx_usb_port_status cvmx_usb_get_status( * Returns: A non-NULL value is a pipe. NULL means an error. */ static struct cvmx_usb_pipe *cvmx_usb_open_pipe(struct cvmx_usb_state *usb, - int device_addr, int - endpoint_num, + int device_addr, + int endpoint_num, enum cvmx_usb_speed device_speed, int max_packet, @@ -1286,7 +1289,8 @@ static struct cvmx_usb_pipe *cvmx_usb_open_pipe(struct cvmx_usb_state *usb, if (__cvmx_usb_pipe_needs_split(usb, pipe)) { pipe->interval = interval*8; /* Force start splits to be schedule on uFrame 0 */ - pipe->next_tx_frame = ((usb->frame_number+7)&~7) + pipe->interval; + pipe->next_tx_frame = ((usb->frame_number+7)&~7) + + pipe->interval; } else { pipe->interval = interval; pipe->next_tx_frame = usb->frame_number + pipe->interval; @@ -1337,9 +1341,13 @@ static void __cvmx_usb_poll_rx_fifo(struct cvmx_usb_state *usb) return; /* Get where the DMA engine would have written this data */ - address = __cvmx_usb_read_csr64(usb, CVMX_USBNX_DMA0_INB_CHN0(usb->index) + channel*8); + address = __cvmx_usb_read_csr64(usb, + CVMX_USBNX_DMA0_INB_CHN0(usb->index) + channel*8); + ptr = cvmx_phys_to_ptr(address); - __cvmx_usb_write_csr64(usb, CVMX_USBNX_DMA0_INB_CHN0(usb->index) + channel*8, address + bytes); + __cvmx_usb_write_csr64(usb, + CVMX_USBNX_DMA0_INB_CHN0(usb->index) + channel*8, + address + bytes); /* Loop writing the FIFO data for this packet into memory */ while (bytes > 0) { @@ -1423,6 +1431,7 @@ static void __cvmx_usb_poll_tx_fifo(struct cvmx_usb_state *usb) { if (usb->periodic.head != usb->periodic.tail) { union cvmx_usbcx_hptxsts tx_status; + tx_status.u32 = __cvmx_usb_read_csr32(usb, CVMX_USBCX_HPTXSTS(usb->index)); if (__cvmx_usb_fill_tx_hw(usb, &usb->periodic, @@ -1438,6 +1447,7 @@ static void __cvmx_usb_poll_tx_fifo(struct cvmx_usb_state *usb) if (usb->nonperiodic.head != usb->nonperiodic.tail) { union cvmx_usbcx_gnptxsts tx_status; + tx_status.u32 = __cvmx_usb_read_csr32(usb, CVMX_USBCX_GNPTXSTS(usb->index)); if (__cvmx_usb_fill_tx_hw(usb, &usb->nonperiodic, @@ -1536,25 +1546,33 @@ static void __cvmx_usb_start_channel_control(struct cvmx_usb_state *usb, switch (transaction->stage) { case CVMX_USB_STAGE_NON_CONTROL: case CVMX_USB_STAGE_NON_CONTROL_SPLIT_COMPLETE: - cvmx_dprintf("%s: ERROR - Non control stage\n", __FUNCTION__); + cvmx_dprintf("%s: ERROR - Non control stage\n", __func__); break; case CVMX_USB_STAGE_SETUP: usbc_hctsiz.s.pid = 3; /* Setup */ bytes_to_transfer = sizeof(*header); /* All Control operations start with a setup going OUT */ - USB_SET_FIELD32(CVMX_USBCX_HCCHARX(channel, usb->index), union cvmx_usbcx_hccharx, epdir, CVMX_USB_DIRECTION_OUT); + USB_SET_FIELD32(CVMX_USBCX_HCCHARX(channel, usb->index), + union cvmx_usbcx_hccharx, epdir, + CVMX_USB_DIRECTION_OUT); /* * Setup send the control header instead of the buffer data. The * buffer data will be used in the next stage */ - __cvmx_usb_write_csr64(usb, CVMX_USBNX_DMA0_OUTB_CHN0(usb->index) + channel*8, transaction->control_header); + __cvmx_usb_write_csr64(usb, + CVMX_USBNX_DMA0_OUTB_CHN0(usb->index) + channel*8, + transaction->control_header); break; case CVMX_USB_STAGE_SETUP_SPLIT_COMPLETE: usbc_hctsiz.s.pid = 3; /* Setup */ bytes_to_transfer = 0; /* All Control operations start with a setup going OUT */ - USB_SET_FIELD32(CVMX_USBCX_HCCHARX(channel, usb->index), union cvmx_usbcx_hccharx, epdir, CVMX_USB_DIRECTION_OUT); - USB_SET_FIELD32(CVMX_USBCX_HCSPLTX(channel, usb->index), union cvmx_usbcx_hcspltx, compsplt, 1); + USB_SET_FIELD32(CVMX_USBCX_HCCHARX(channel, usb->index), + union cvmx_usbcx_hccharx, epdir, + CVMX_USB_DIRECTION_OUT); + + USB_SET_FIELD32(CVMX_USBCX_HCSPLTX(channel, usb->index), + union cvmx_usbcx_hcspltx, compsplt, 1); break; case CVMX_USB_STAGE_DATA: usbc_hctsiz.s.pid = __cvmx_usb_get_data_pid(pipe); @@ -1684,6 +1702,7 @@ static void __cvmx_usb_start_channel(struct cvmx_usb_state *usb, /* Clear all channel status bits */ usbc_hcint.u32 = __cvmx_usb_read_csr32(usb, CVMX_USBCX_HCINTX(channel, usb->index)); + __cvmx_usb_write_csr32(usb, CVMX_USBCX_HCINTX(channel, usb->index), usbc_hcint.u32); @@ -1714,18 +1733,31 @@ static void __cvmx_usb_start_channel(struct cvmx_usb_state *usb, __cvmx_usb_write_csr32(usb, CVMX_USBCX_HCINTMSKX(channel, usb->index), usbc_hcintmsk.u32); /* Enable the channel interrupt to propagate */ - usbc_haintmsk.u32 = __cvmx_usb_read_csr32(usb, CVMX_USBCX_HAINTMSK(usb->index)); + usbc_haintmsk.u32 = __cvmx_usb_read_csr32(usb, + CVMX_USBCX_HAINTMSK(usb->index)); usbc_haintmsk.s.haintmsk |= 1<<channel; - __cvmx_usb_write_csr32(usb, CVMX_USBCX_HAINTMSK(usb->index), usbc_haintmsk.u32); + __cvmx_usb_write_csr32(usb, + CVMX_USBCX_HAINTMSK(usb->index), + usbc_haintmsk.u32); } /* Setup the locations the DMA engines use */ { - uint64_t dma_address = transaction->buffer + transaction->actual_bytes; + uint64_t dma_address = transaction->buffer + + transaction->actual_bytes; + if (transaction->type == CVMX_USB_TRANSFER_ISOCHRONOUS) - dma_address = transaction->buffer + transaction->iso_packets[0].offset + transaction->actual_bytes; - __cvmx_usb_write_csr64(usb, CVMX_USBNX_DMA0_OUTB_CHN0(usb->index) + channel*8, dma_address); - __cvmx_usb_write_csr64(usb, CVMX_USBNX_DMA0_INB_CHN0(usb->index) + channel*8, dma_address); + dma_address = transaction->buffer + + transaction->iso_packets[0].offset + + transaction->actual_bytes; + + __cvmx_usb_write_csr64(usb, + CVMX_USBNX_DMA0_OUTB_CHN0(usb->index) + channel*8, + dma_address); + + __cvmx_usb_write_csr64(usb, + CVMX_USBNX_DMA0_INB_CHN0(usb->index) + channel*8, + dma_address); } /* Setup both the size of the transfer and the SPLIT characteristics */ @@ -2008,8 +2040,7 @@ static struct cvmx_usb_pipe *__cvmx_usb_find_ready_pipe( ((((int)current_frame - (int)pipe->split_sc_frame) & 0x7f) < 0x40)) && (!usb->active_split || (usb->active_split == t))) { - CVMX_PREFETCH(pipe, 128); - CVMX_PREFETCH(t, 0); + prefetch(t); return pipe; } } @@ -2036,8 +2067,16 @@ static void __cvmx_usb_schedule(struct cvmx_usb_state *usb, int is_sof) * Without DMA we need to be careful to not schedule something * at the end of a frame and cause an overrun. */ - union cvmx_usbcx_hfnum hfnum = {.u32 = __cvmx_usb_read_csr32(usb, CVMX_USBCX_HFNUM(usb->index))}; - union cvmx_usbcx_hfir hfir = {.u32 = __cvmx_usb_read_csr32(usb, CVMX_USBCX_HFIR(usb->index))}; + union cvmx_usbcx_hfnum hfnum = { + .u32 = __cvmx_usb_read_csr32(usb, + CVMX_USBCX_HFNUM(usb->index)) + }; + + union cvmx_usbcx_hfir hfir = { + .u32 = __cvmx_usb_read_csr32(usb, + CVMX_USBCX_HFIR(usb->index)) + }; + if (hfnum.s.frrem < hfir.s.frint/4) goto done; } @@ -2522,6 +2561,7 @@ static int cvmx_usb_cancel_all(struct cvmx_usb_state *usb, /* Simply loop through and attempt to cancel each transaction */ list_for_each_entry_safe(transaction, next, &pipe->transactions, node) { int result = cvmx_usb_cancel(usb, pipe, transaction); + if (unlikely(result != 0)) return result; } @@ -2654,19 +2694,20 @@ static int __cvmx_usb_poll_channel(struct cvmx_usb_state *usb, int channel) } /* Disable the channel interrupts now that it is done */ - __cvmx_usb_write_csr32(usb, CVMX_USBCX_HCINTMSKX(channel, usb->index), 0); + __cvmx_usb_write_csr32(usb, + CVMX_USBCX_HCINTMSKX(channel, usb->index), + 0); usb->idle_hardware_channels |= (1<<channel); /* Make sure this channel is tied to a valid pipe */ pipe = usb->pipe_for_channel[channel]; - CVMX_PREFETCH(pipe, 0); - CVMX_PREFETCH(pipe, 128); + prefetch(pipe); if (!pipe) return 0; transaction = list_first_entry(&pipe->transactions, typeof(*transaction), node); - CVMX_PREFETCH(transaction, 0); + prefetch(transaction); /* * Disconnect this pipe from the HW channel. Later the schedule @@ -3021,7 +3062,8 @@ static int __cvmx_usb_poll_channel(struct cvmx_usb_state *usb, int channel) */ if (!buffer_space_left || (bytes_this_transfer < 188)) { - pipe->next_tx_frame += pipe->interval; + pipe->next_tx_frame += + pipe->interval; __cvmx_usb_perform_complete( usb, pipe, @@ -3083,6 +3125,7 @@ static int __cvmx_usb_poll_channel(struct cvmx_usb_state *usb, int channel) pipe->interval; } else { struct cvmx_usb_port_status port; + port = cvmx_usb_get_status(usb); if (port.port_enabled) { /* We'll retry the exact same transaction again */ @@ -3123,24 +3166,24 @@ static int cvmx_usb_poll(struct cvmx_usb_state *usb) union cvmx_usbcx_hfnum usbc_hfnum; union cvmx_usbcx_gintsts usbc_gintsts; - CVMX_PREFETCH(usb, 0); - CVMX_PREFETCH(usb, 1*128); - CVMX_PREFETCH(usb, 2*128); - CVMX_PREFETCH(usb, 3*128); - CVMX_PREFETCH(usb, 4*128); + prefetch_range(usb, sizeof(*usb)); /* Update the frame counter */ - usbc_hfnum.u32 = __cvmx_usb_read_csr32(usb, CVMX_USBCX_HFNUM(usb->index)); + usbc_hfnum.u32 = __cvmx_usb_read_csr32(usb, + CVMX_USBCX_HFNUM(usb->index)); if ((usb->frame_number&0x3fff) > usbc_hfnum.s.frnum) usb->frame_number += 0x4000; usb->frame_number &= ~0x3fffull; usb->frame_number |= usbc_hfnum.s.frnum; /* Read the pending interrupts */ - usbc_gintsts.u32 = __cvmx_usb_read_csr32(usb, CVMX_USBCX_GINTSTS(usb->index)); + usbc_gintsts.u32 = __cvmx_usb_read_csr32(usb, + CVMX_USBCX_GINTSTS(usb->index)); /* Clear the interrupts now that we know about them */ - __cvmx_usb_write_csr32(usb, CVMX_USBCX_GINTSTS(usb->index), usbc_gintsts.u32); + __cvmx_usb_write_csr32(usb, + CVMX_USBCX_GINTSTS(usb->index), + usbc_gintsts.u32); if (usbc_gintsts.s.rxflvl) { /* @@ -3196,7 +3239,9 @@ static int cvmx_usb_poll(struct cvmx_usb_state *usb) * to clear this bit. */ union cvmx_usbcx_haint usbc_haint; - usbc_haint.u32 = __cvmx_usb_read_csr32(usb, CVMX_USBCX_HAINT(usb->index)); + + usbc_haint.u32 = __cvmx_usb_read_csr32(usb, + CVMX_USBCX_HAINT(usb->index)); while (usbc_haint.u32) { int channel; @@ -3267,6 +3312,7 @@ static int octeon_usb_urb_enqueue(struct usb_hcd *hcd, enum cvmx_usb_speed speed; int split_device = 0; int split_port = 0; + switch (usb_pipetype(urb->pipe)) { case PIPE_ISOCHRONOUS: transfer_type = CVMX_USB_TRANSFER_ISOCHRONOUS; @@ -3303,6 +3349,7 @@ static int octeon_usb_urb_enqueue(struct usb_hcd *hcd, * tree. */ struct usb_device *dev = urb->dev; + while (dev->parent) { /* * If our parent is high speed then he'll @@ -3462,6 +3509,7 @@ static void octeon_usb_endpoint_disable(struct usb_hcd *hcd, struct octeon_hcd *priv = hcd_to_octeon(hcd); struct cvmx_usb_pipe *pipe = ep->hcpriv; unsigned long flags; + spin_lock_irqsave(&priv->lock, flags); cvmx_usb_cancel_all(&priv->usb, pipe); if (cvmx_usb_close_pipe(&priv->usb, pipe)) @@ -3483,10 +3531,11 @@ static int octeon_usb_hub_status_data(struct usb_hcd *hcd, char *buf) buf[0] = 0; buf[0] = port_status.connect_change << 1; - return (buf[0] != 0); + return buf[0] != 0; } -static int octeon_usb_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, u16 wIndex, char *buf, u16 wLength) +static int octeon_usb_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, + u16 wIndex, char *buf, u16 wLength) { struct octeon_hcd *priv = hcd_to_octeon(hcd); struct device *dev = hcd->self.controller; @@ -3778,6 +3827,7 @@ static int octeon_usb_probe(struct platform_device *pdev) if (irq < 0) { /* Defective device tree, but we know how to fix it. */ irq_hw_number_t hwirq = usb_num ? (1 << 6) + 17 : 56; + irq = irq_create_mapping(NULL, hwirq); } |