aboutsummaryrefslogtreecommitdiff
path: root/lib/libusb
diff options
context:
space:
mode:
authorNikunj A Dadhania <nikunj@linux.vnet.ibm.com>2013-07-24 14:27:02 +0530
committerNikunj A Dadhania <nikunj@linux.vnet.ibm.com>2013-07-24 14:46:23 +0530
commit323108d7929c69cc934eb59aee7b5226a49926e9 (patch)
treeec056de9037c5389d87bcfdb0511fc361c7369f5 /lib/libusb
parent91826241af54c7c7adbfc0480d5dde918d8949d8 (diff)
downloadSLOF-323108d7929c69cc934eb59aee7b5226a49926e9.zip
SLOF-323108d7929c69cc934eb59aee7b5226a49926e9.tar.gz
SLOF-323108d7929c69cc934eb59aee7b5226a49926e9.tar.bz2
usb-ohci: implement ohci send control
Signed-off-by: Nikunj A Dadhania <nikunj@linux.vnet.ibm.com> Acked-by: Thomas Huth <thuth@linux.vnet.ibm.com>
Diffstat (limited to 'lib/libusb')
-rw-r--r--lib/libusb/usb-ohci.c118
-rw-r--r--lib/libusb/usb-ohci.h3
2 files changed, 118 insertions, 3 deletions
diff --git a/lib/libusb/usb-ohci.c b/lib/libusb/usb-ohci.c
index f259927..6fe0181 100644
--- a/lib/libusb/usb-ohci.c
+++ b/lib/libusb/usb-ohci.c
@@ -181,7 +181,7 @@ static inline struct ohci_ed *ohci_pipe_get_ed(struct usb_pipe *pipe)
struct ohci_pipe *opipe;
opipe = container_of(pipe, struct ohci_pipe, pipe);
-
+ dprintf("%s: ed is %p\n", __func__, &opipe->ed);
return &opipe->ed;
}
@@ -296,9 +296,121 @@ static void ohci_disconnect(void)
}
+#define OHCI_CTRL_TDS 3
+
+static void ohci_fill_td(struct ohci_td *td, long next,
+ long req, size_t size, unsigned int attr)
+{
+ if (size && req) {
+ write_reg(&td->cbp, req);
+ write_reg(&td->be, req + size - 1);
+ } else {
+ td->cbp = 0;
+ td->be = 0;
+ }
+ write_reg(&td->attr, attr);
+ write_reg(&td->next_td, next);
+
+ dprintf("%s: cbp %08X attr %08X next_td %08X be %08X\n", __func__,
+ read_reg(&td->cbp), read_reg(&td->attr),
+ read_reg(&td->next_td), read_reg(&td->be));
+}
+
+static void ohci_fill_ed(struct ohci_ed *ed, long headp, long tailp,
+ unsigned int attr, long next_ed)
+{
+ write_reg(&ed->attr, attr);
+ write_reg(&ed->headp, headp);
+ write_reg(&ed->tailp, tailp);
+ write_reg(&ed->next_ed, next_ed);
+}
+
+static long ohci_get_td_phys(struct ohci_td *curr, struct ohci_td *start, long td_phys)
+{
+ //dprintf("position %d\n", curr - start);
+ return td_phys + (curr - start) * sizeof(*start);
+}
+
+/*
+ * OHCI Spec:
+ * 4.2 Endpoint Descriptor
+ * 4.3.1 General Transfer Descriptor
+ * 5.2.8 Transfer Descriptor Queues
+ */
static int ohci_send_ctrl(struct usb_pipe *pipe, struct usb_dev_req *req, void *data)
{
- return false;
+ struct ohci_ed *ed;
+ struct ohci_td *tds, *td, *td_phys;
+ struct ohci_regs *regs;
+ struct ohci_hcd *ohcd;
+ uint32_t datalen;
+ uint32_t dir, attr = 0;
+ uint32_t time;
+ int ret = true;
+ long req_phys = 0, data_phys = 0, td_next = 0;
+
+ datalen = read_reg16(&req->wLength);
+ dir = (req->bmRequestType & REQT_DIR_IN) ? 1 : 0;
+
+ dprintf("usb-ohci: %s len %d DIR_IN %d\n", __func__, datalen, dir);
+
+ tds = td = (struct ohci_td *) SLOF_dma_alloc(sizeof(*td) * OHCI_CTRL_TDS);
+ td_phys = (struct ohci_td *) SLOF_dma_map_in(td, sizeof(*td) * OHCI_CTRL_TDS, true);
+ memset(td, 0, sizeof(*td) * OHCI_CTRL_TDS);
+
+ req_phys = SLOF_dma_map_in(req, sizeof(struct usb_dev_req), true);
+ attr = TDA_DP_SETUP | TDA_CC | TDA_TOGGLE_DATA0;
+ td_next = ohci_get_td_phys(td + 1, tds, PTR_U32(td_phys));
+ ohci_fill_td(td, td_next, req_phys, sizeof(*req), attr);
+ td++;
+
+ if (datalen) {
+ data_phys = SLOF_dma_map_in(data, datalen, true);
+ attr = 0;
+ attr = (dir ? TDA_DP_IN : TDA_DP_OUT) | TDA_TOGGLE_DATA1 | TDA_CC;
+ td_next = ohci_get_td_phys(td + 1, tds, PTR_U32(td_phys));
+ ohci_fill_td(td, td_next, data_phys, datalen, attr);
+ td++;
+ }
+
+ attr = 0;
+ attr = (dir ? TDA_DP_OUT : TDA_DP_IN) | TDA_CC | TDA_TOGGLE_DATA1;
+ td_next = ohci_get_td_phys(td + 1, tds, PTR_U32(td_phys));
+ ohci_fill_td(td, td_next, 0, 0, attr);
+ td++;
+
+ ed = ohci_pipe_get_ed(pipe);
+ attr = 0;
+ attr = EDA_FADDR(pipe->dev->addr) | EDA_MPS(pipe->mps) | EDA_SKIP;
+ ohci_fill_ed(ed, PTR_U32(td_phys), td_next, attr, 0);
+ dprintf("usb-ohci: %s - td_start %x td_end %x req %x\n", __func__,
+ td_phys, td_next, req_phys);
+ barrier();
+ write_reg(&ed->attr, read_reg(&ed->attr) & ~EDA_SKIP);
+
+ ohcd = pipe->dev->hcidev->priv;
+ regs = ohcd->regs;
+ write_reg(&regs->cntl_head_ed, ohci_pipe_get_ed_phys(pipe));
+ write_reg(&regs->cmd_status, OHCI_CMD_STATUS_CLF);
+
+ time = SLOF_GetTimer() + 20;
+ while ((time > SLOF_GetTimer()) &&
+ ((ed->headp & EDA_HEADP_MASK_LE) != ed->tailp))
+ cpu_relax();
+
+ if ((ed->headp & EDA_HEADP_MASK_LE) == ed->tailp)
+ dprintf("%s: packet sent\n", __func__);
+ else {
+ printf("%s: timed out - failed headp %08x tailp %08x\n",
+ __func__, ed->headp, ed->tailp);
+ ret = false;
+ }
+
+ SLOF_dma_map_out(req_phys, req, sizeof(struct usb_dev_req));
+ if (datalen)
+ SLOF_dma_map_out(data_phys, data, datalen);
+ SLOF_dma_map_out(PTR_U32(td_phys), td, sizeof(*td) * OHCI_CTRL_TDS);
+ return ret;
}
static struct usb_pipe *ohci_get_pipe(struct usb_dev *dev, struct usb_ep_descr *ep,
@@ -330,7 +442,7 @@ static struct usb_pipe *ohci_get_pipe(struct usb_dev *dev, struct usb_ep_descr *
new->speed = dev->speed;
new->mps = read_reg16(&ep->wMaxPacketSize);
new->dir = ep->bEndpointAddress & 0x80;
- dprintf("usb-ohci: %s exit\n", __func__);
+ dprintf("usb-ohci: %s exit %p\n", __func__, new);
return new;
}
diff --git a/lib/libusb/usb-ohci.h b/lib/libusb/usb-ohci.h
index 8d63493..75d8c8f 100644
--- a/lib/libusb/usb-ohci.h
+++ b/lib/libusb/usb-ohci.h
@@ -59,6 +59,9 @@ struct ohci_regs {
#define EDA_FORMAT_ISO (1 << 15)
#define EDA_MPS(x) ((x & 0x7FF) << 16)
+#define EDA_HEADP_MASK (0xFFFFFFFC)
+#define EDA_HEADP_MASK_LE (cpu_to_le32(EDA_HEADP_MASK))
+
struct ohci_ed {
uint32_t attr;
uint32_t tailp;