summaryrefslogtreecommitdiff
path: root/drivers/usb/gadget/fsl_udc_core.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/usb/gadget/fsl_udc_core.c')
-rw-r--r--drivers/usb/gadget/fsl_udc_core.c123
1 files changed, 98 insertions, 25 deletions
diff --git a/drivers/usb/gadget/fsl_udc_core.c b/drivers/usb/gadget/fsl_udc_core.c
index 07499c1..b101b7e 100644
--- a/drivers/usb/gadget/fsl_udc_core.c
+++ b/drivers/usb/gadget/fsl_udc_core.c
@@ -77,12 +77,64 @@ fsl_ep0_desc = {
static void fsl_ep_fifo_flush(struct usb_ep *_ep);
#ifdef CONFIG_PPC32
-#define fsl_readl(addr) in_le32(addr)
-#define fsl_writel(val32, addr) out_le32(addr, val32)
-#else
+/*
+ * On some SoCs, the USB controller registers can be big or little endian,
+ * depending on the version of the chip. In order to be able to run the
+ * same kernel binary on 2 different versions of an SoC, the BE/LE decision
+ * must be made at run time. _fsl_readl and fsl_writel are pointers to the
+ * BE or LE readl() and writel() functions, and fsl_readl() and fsl_writel()
+ * call through those pointers. Platform code for SoCs that have BE USB
+ * registers should set pdata->big_endian_mmio flag.
+ *
+ * This also applies to controller-to-cpu accessors for the USB descriptors,
+ * since their endianness is also SoC dependant. Platform code for SoCs that
+ * have BE USB descriptors should set pdata->big_endian_desc flag.
+ */
+static u32 _fsl_readl_be(const unsigned __iomem *p)
+{
+ return in_be32(p);
+}
+
+static u32 _fsl_readl_le(const unsigned __iomem *p)
+{
+ return in_le32(p);
+}
+
+static void _fsl_writel_be(u32 v, unsigned __iomem *p)
+{
+ out_be32(p, v);
+}
+
+static void _fsl_writel_le(u32 v, unsigned __iomem *p)
+{
+ out_le32(p, v);
+}
+
+static u32 (*_fsl_readl)(const unsigned __iomem *p);
+static void (*_fsl_writel)(u32 v, unsigned __iomem *p);
+
+#define fsl_readl(p) (*_fsl_readl)((p))
+#define fsl_writel(v, p) (*_fsl_writel)((v), (p))
+
+static inline u32 cpu_to_hc32(const u32 x)
+{
+ return udc_controller->pdata->big_endian_desc
+ ? (__force u32)cpu_to_be32(x)
+ : (__force u32)cpu_to_le32(x);
+}
+
+static inline u32 hc32_to_cpu(const u32 x)
+{
+ return udc_controller->pdata->big_endian_desc
+ ? be32_to_cpu((__force __be32)x)
+ : le32_to_cpu((__force __le32)x);
+}
+#else /* !CONFIG_PPC32 */
#define fsl_readl(addr) readl(addr)
#define fsl_writel(val32, addr) writel(val32, addr)
-#endif
+#define cpu_to_hc32(x) cpu_to_le32(x)
+#define hc32_to_cpu(x) le32_to_cpu(x)
+#endif /* CONFIG_PPC32 */
/********************************************************************
* Internal Used Function
@@ -409,7 +461,7 @@ static void struct_ep_qh_setup(struct fsl_udc *udc, unsigned char ep_num,
if (zlt)
tmp |= EP_QUEUE_HEAD_ZLT_SEL;
- p_QH->max_pkt_length = cpu_to_le32(tmp);
+ p_QH->max_pkt_length = cpu_to_hc32(tmp);
p_QH->next_dtd_ptr = 1;
p_QH->size_ioc_int_sts = 0;
}
@@ -616,7 +668,7 @@ static void fsl_queue_td(struct fsl_ep *ep, struct fsl_req *req)
struct fsl_req *lastreq;
lastreq = list_entry(ep->queue.prev, struct fsl_req, queue);
lastreq->tail->next_td_ptr =
- cpu_to_le32(req->head->td_dma & DTD_ADDR_MASK);
+ cpu_to_hc32(req->head->td_dma & DTD_ADDR_MASK);
/* Read prime bit, if 1 goto done */
if (fsl_readl(&dr_regs->endpointprime) & bitmask)
goto out;
@@ -641,10 +693,10 @@ static void fsl_queue_td(struct fsl_ep *ep, struct fsl_req *req)
/* Write dQH next pointer and terminate bit to 0 */
temp = req->head->td_dma & EP_QUEUE_HEAD_NEXT_POINTER_MASK;
- dQH->next_dtd_ptr = cpu_to_le32(temp);
+ dQH->next_dtd_ptr = cpu_to_hc32(temp);
/* Clear active and halt bit */
- temp = cpu_to_le32(~(EP_QUEUE_HEAD_STATUS_ACTIVE
+ temp = cpu_to_hc32(~(EP_QUEUE_HEAD_STATUS_ACTIVE
| EP_QUEUE_HEAD_STATUS_HALT));
dQH->size_ioc_int_sts &= temp;
@@ -682,17 +734,17 @@ static struct ep_td_struct *fsl_build_dtd(struct fsl_req *req, unsigned *length,
dtd->td_dma = *dma;
/* Clear reserved field */
- swap_temp = cpu_to_le32(dtd->size_ioc_sts);
+ swap_temp = hc32_to_cpu(dtd->size_ioc_sts);
swap_temp &= ~DTD_RESERVED_FIELDS;
- dtd->size_ioc_sts = cpu_to_le32(swap_temp);
+ dtd->size_ioc_sts = cpu_to_hc32(swap_temp);
/* Init all of buffer page pointers */
swap_temp = (u32) (req->req.dma + req->req.actual);
- dtd->buff_ptr0 = cpu_to_le32(swap_temp);
- dtd->buff_ptr1 = cpu_to_le32(swap_temp + 0x1000);
- dtd->buff_ptr2 = cpu_to_le32(swap_temp + 0x2000);
- dtd->buff_ptr3 = cpu_to_le32(swap_temp + 0x3000);
- dtd->buff_ptr4 = cpu_to_le32(swap_temp + 0x4000);
+ dtd->buff_ptr0 = cpu_to_hc32(swap_temp);
+ dtd->buff_ptr1 = cpu_to_hc32(swap_temp + 0x1000);
+ dtd->buff_ptr2 = cpu_to_hc32(swap_temp + 0x2000);
+ dtd->buff_ptr3 = cpu_to_hc32(swap_temp + 0x3000);
+ dtd->buff_ptr4 = cpu_to_hc32(swap_temp + 0x4000);
req->req.actual += *length;
@@ -716,7 +768,7 @@ static struct ep_td_struct *fsl_build_dtd(struct fsl_req *req, unsigned *length,
if (*is_last && !req->req.no_interrupt)
swap_temp |= DTD_IOC;
- dtd->size_ioc_sts = cpu_to_le32(swap_temp);
+ dtd->size_ioc_sts = cpu_to_hc32(swap_temp);
mb();
@@ -743,7 +795,7 @@ static int fsl_req_to_dtd(struct fsl_req *req)
is_first = 0;
req->head = dtd;
} else {
- last_dtd->next_td_ptr = cpu_to_le32(dma);
+ last_dtd->next_td_ptr = cpu_to_hc32(dma);
last_dtd->next_td_virt = dtd;
}
last_dtd = dtd;
@@ -751,7 +803,7 @@ static int fsl_req_to_dtd(struct fsl_req *req)
req->dtd_count++;
} while (!is_last);
- dtd->next_td_ptr = cpu_to_le32(DTD_NEXT_TERMINATE);
+ dtd->next_td_ptr = cpu_to_hc32(DTD_NEXT_TERMINATE);
req->tail = dtd;
@@ -1394,6 +1446,7 @@ static void tripwire_handler(struct fsl_udc *udc, u8 ep_num, u8 *buffer_ptr)
{
u32 temp;
struct ep_queue_head *qh;
+ struct fsl_usb2_platform_data *pdata = udc->pdata;
qh = &udc->ep_qh[ep_num * 2 + EP_DIR_OUT];
@@ -1408,7 +1461,16 @@ static void tripwire_handler(struct fsl_udc *udc, u8 ep_num, u8 *buffer_ptr)
fsl_writel(temp | USB_CMD_SUTW, &dr_regs->usbcmd);
/* Copy the setup packet to local buffer */
- memcpy(buffer_ptr, (u8 *) qh->setup_buffer, 8);
+ if (pdata->le_setup_buf) {
+ u32 *p = (u32 *)buffer_ptr;
+ u32 *s = (u32 *)qh->setup_buffer;
+
+ /* Convert little endian setup buffer to CPU endian */
+ *p++ = le32_to_cpu(*s++);
+ *p = le32_to_cpu(*s);
+ } else {
+ memcpy(buffer_ptr, (u8 *) qh->setup_buffer, 8);
+ }
} while (!(fsl_readl(&dr_regs->usbcmd) & USB_CMD_SUTW));
/* Clear Setup Tripwire */
@@ -1432,19 +1494,19 @@ static int process_ep_req(struct fsl_udc *udc, int pipe,
actual = curr_req->req.length;
for (j = 0; j < curr_req->dtd_count; j++) {
- remaining_length = (le32_to_cpu(curr_td->size_ioc_sts)
+ remaining_length = (hc32_to_cpu(curr_td->size_ioc_sts)
& DTD_PACKET_SIZE)
>> DTD_LENGTH_BIT_POS;
actual -= remaining_length;
- if ((errors = le32_to_cpu(curr_td->size_ioc_sts) &
- DTD_ERROR_MASK)) {
+ errors = hc32_to_cpu(curr_td->size_ioc_sts);
+ if (errors & DTD_ERROR_MASK) {
if (errors & DTD_STATUS_HALTED) {
ERR("dTD error %08x QH=%d\n", errors, pipe);
/* Clear the errors and Halt condition */
- tmp = le32_to_cpu(curr_qh->size_ioc_int_sts);
+ tmp = hc32_to_cpu(curr_qh->size_ioc_int_sts);
tmp &= ~errors;
- curr_qh->size_ioc_int_sts = cpu_to_le32(tmp);
+ curr_qh->size_ioc_int_sts = cpu_to_hc32(tmp);
status = -EPIPE;
/* FIXME: continue with next queued TD? */
@@ -1462,7 +1524,7 @@ static int process_ep_req(struct fsl_udc *udc, int pipe,
ERR("Unknown error has occurred (0x%x)!\n",
errors);
- } else if (le32_to_cpu(curr_td->size_ioc_sts)
+ } else if (hc32_to_cpu(curr_td->size_ioc_sts)
& DTD_STATUS_ACTIVE) {
VDBG("Request not complete");
status = REQ_UNCOMPLETE;
@@ -2233,6 +2295,7 @@ static int __init struct_ep_setup(struct fsl_udc *udc, unsigned char index,
*/
static int __init fsl_udc_probe(struct platform_device *pdev)
{
+ struct fsl_usb2_platform_data *pdata;
struct resource *res;
int ret = -ENODEV;
unsigned int i;
@@ -2249,6 +2312,8 @@ static int __init fsl_udc_probe(struct platform_device *pdev)
return -ENOMEM;
}
+ pdata = pdev->dev.platform_data;
+ udc_controller->pdata = pdata;
spin_lock_init(&udc_controller->lock);
udc_controller->stopped = 1;
@@ -2271,6 +2336,14 @@ static int __init fsl_udc_probe(struct platform_device *pdev)
goto err_release_mem_region;
}
+ if (pdata->big_endian_mmio) {
+ _fsl_readl = _fsl_readl_be;
+ _fsl_writel = _fsl_writel_be;
+ } else {
+ _fsl_readl = _fsl_readl_le;
+ _fsl_writel = _fsl_writel_le;
+ }
+
#ifndef CONFIG_ARCH_MXC
usb_sys_regs = (struct usb_sys_interface *)
((u32)dr_regs + USB_DR_SYS_OFFSET);