summaryrefslogtreecommitdiffstats
path: root/drivers/usb/host/ehci-hcd.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/usb/host/ehci-hcd.c')
-rw-r--r--drivers/usb/host/ehci-hcd.c213
1 files changed, 134 insertions, 79 deletions
diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c
index f3be177ceb..7ae3a285a0 100644
--- a/drivers/usb/host/ehci-hcd.c
+++ b/drivers/usb/host/ehci-hcd.c
@@ -1,26 +1,16 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*-
* Copyright (c) 2007-2008, Juniper Networks, Inc.
* Copyright (c) 2008, Excito Elektronik i Skåne AB
* Copyright (c) 2008, Michael Trimarchi <trimarchimichael@yahoo.it>
*
* All rights reserved.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation version 2 of
- * the License.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
*/
/*#define DEBUG */
#include <common.h>
#include <dma.h>
#include <asm/byteorder.h>
-#include <usb/usb.h>
+#include <linux/usb/usb.h>
#include <io.h>
#include <malloc.h>
#include <driver.h>
@@ -29,20 +19,24 @@
#include <clock.h>
#include <errno.h>
#include <of.h>
-#include <usb/ehci.h>
+#include <linux/usb/ehci.h>
#include <linux/err.h>
#include <linux/sizes.h>
+#include <linux/clk.h>
+#include <linux/phy/phy.h>
#include "ehci.h"
struct ehci_host {
int rootdev;
- struct device_d *dev;
+ struct device *dev;
struct ehci_hccr *hccr;
struct ehci_hcor *hcor;
struct usb_host host;
struct QH *qh_list;
+ dma_addr_t qh_list_dma;
struct qTD *td;
+ dma_addr_t td_dma;
int portreset;
unsigned long flags;
@@ -51,7 +45,9 @@ struct ehci_host {
void *drvdata;
int periodic_schedules;
struct QH *periodic_queue;
+ dma_addr_t periodic_queue_dma;
uint32_t *periodic_list;
+ dma_addr_t periodic_list_dma;
};
struct int_queue {
@@ -59,9 +55,11 @@ struct int_queue {
int queuesize;
unsigned long pipe;
struct QH *first;
+ dma_addr_t first_dma;
struct QH *current;
struct QH *last;
struct qTD *tds;
+ dma_addr_t tds_dma;
};
#define to_ehci(ptr) container_of(ptr, struct ehci_host, host)
@@ -137,6 +135,38 @@ static struct descriptor {
#define ehci_is_TDI() (ehci->flags & EHCI_HAS_TT)
+#define EHCI_DMA(dma0, ptr0, ptr) \
+ ((dma0) + ((ptr) - (ptr0)) * sizeof(*(ptr)))
+
+static inline uint32_t ehci_qh_dma(struct ehci_host *ehci, struct QH *qh)
+{
+ return EHCI_DMA(ehci->qh_list_dma, ehci->qh_list, qh);
+}
+
+static inline uint32_t ehci_td_dma(struct ehci_host *ehci, struct qTD *td)
+{
+ return EHCI_DMA(ehci->td_dma, ehci->td, td);
+}
+
+static inline uint32_t ehci_int_qh_dma(struct int_queue *intq, struct QH *qh)
+{
+ return EHCI_DMA(intq->first_dma, intq->first, qh);
+}
+
+static inline uint32_t ehci_int_td_dma(struct int_queue *intq, struct qTD *td)
+{
+ return EHCI_DMA(intq->tds_dma, intq->tds, td);
+}
+
+static void memzero32(void *ptr, size_t size)
+{
+ uint32_t *ptr32 = ptr;
+ int i;
+
+ for (i = 0; i < size / sizeof(uint32_t); i++)
+ ptr32[i] = 0x0;
+}
+
static int handshake(uint32_t *ptr, uint32_t mask, uint32_t done, int usec)
{
uint32_t result;
@@ -202,7 +232,7 @@ static int ehci_td_buffer(struct qTD *td, dma_addr_t addr, size_t sz)
}
if (idx == buffer_count) {
- pr_debug("out of buffer pointers (%u bytes left)\n", sz);
+ pr_debug("out of buffer pointers (%zu bytes left)\n", sz);
return -ENOMEM;
}
@@ -212,7 +242,7 @@ static int ehci_td_buffer(struct qTD *td, dma_addr_t addr, size_t sz)
return 0;
}
-static int ehci_prepare_qtd(struct device_d *dev,
+static int ehci_prepare_qtd(struct device *dev,
struct qTD *td, uint32_t token,
void *buffer, size_t length,
dma_addr_t *buffer_dma,
@@ -237,7 +267,7 @@ static int ehci_prepare_qtd(struct device_d *dev,
if (ret)
return ret;
} else {
- memset(td->qt_buffer, 0, sizeof(td->qt_buffer));
+ memzero32(td->qt_buffer, sizeof(td->qt_buffer));
}
return 0;
@@ -267,15 +297,17 @@ ehci_submit_async(struct usb_device *dev, unsigned long pipe, void *buffer,
struct usb_host *host = dev->host;
struct ehci_host *ehci = to_ehci(host);
const bool dir_in = usb_pipein(pipe);
- dma_addr_t buffer_dma, req_dma;
+ dma_addr_t buffer_dma = DMA_ERROR_CODE, req_dma;
struct QH *qh = &ehci->qh_list[1];
struct qTD *td;
+ volatile struct qTD *vtd;
uint32_t *tdp;
uint32_t endpt, token, usbsts;
uint32_t status;
uint32_t toggle;
bool c;
int ret;
+ uint64_t start, timeout_val;
dev_dbg(ehci->dev, "pipe=%lx, buffer=%p, length=%d, req=%p\n", pipe,
@@ -318,7 +350,7 @@ ehci_submit_async(struct usb_device *dev, unsigned long pipe, void *buffer,
qh->qh_endpt2 = cpu_to_hc32(endpt);
qh->qh_curtd = 0;
qh->qt_token = 0;
- memset(qh->qt_buffer, 0, sizeof(qh->qt_buffer));
+ memzero32(qh->qt_buffer, sizeof(qh->qt_buffer));
tdp = &qh->qt_next;
@@ -337,7 +369,7 @@ ehci_submit_async(struct usb_device *dev, unsigned long pipe, void *buffer,
dev_dbg(ehci->dev, "unable construct SETUP td\n");
return ret;
}
- *tdp = cpu_to_hc32((uint32_t) td);
+ *tdp = cpu_to_hc32(ehci_td_dma(ehci, td));
tdp = &td->qt_next;
toggle = 1;
@@ -376,7 +408,7 @@ ehci_submit_async(struct usb_device *dev, unsigned long pipe, void *buffer,
dev_err(ehci->dev, "unable construct DATA td\n");
return ret;
}
- *tdp = cpu_to_hc32((uint32_t) td);
+ *tdp = cpu_to_hc32(ehci_td_dma(ehci, td));
tdp = &td->qt_next;
}
@@ -390,7 +422,7 @@ ehci_submit_async(struct usb_device *dev, unsigned long pipe, void *buffer,
QT_TOKEN_PID_IN),
NULL, 0,
NULL, DMA_NONE);
- *tdp = cpu_to_hc32((uint32_t)td);
+ *tdp = cpu_to_hc32(ehci_td_dma(ehci, td));
tdp = &td->qt_next;
}
@@ -404,13 +436,18 @@ ehci_submit_async(struct usb_device *dev, unsigned long pipe, void *buffer,
return ret;
}
- ret = handshake(&ehci->hcor->or_usbsts, STS_USBINT, STS_USBINT,
- timeout_ms * 1000);
- if (ret < 0) {
- ehci_enable_async_schedule(ehci, false);
- ehci_writel(&qh->qt_token, 0);
- return -ETIMEDOUT;
- }
+ /* Wait for TDs to be processed. */
+ timeout_val = timeout_ms * MSECOND;
+ start = get_time_ns();
+ vtd = td;
+ do {
+ token = hc32_to_cpu(vtd->qt_token);
+ if (is_timeout_non_interruptible(start, timeout_val)) {
+ ehci_enable_async_schedule(ehci, false);
+ ehci_writel(&qh->qt_token, 0);
+ return -ETIMEDOUT;
+ }
+ } while (token & QT_TOKEN_STATUS_ACTIVE);
if (req)
dma_unmap_single(ehci->dev, req_dma, sizeof(*req),
@@ -473,7 +510,7 @@ ehci_submit_async(struct usb_device *dev, unsigned long pipe, void *buffer,
}
#if defined(CONFIG_MACH_EFIKA_MX_SMARTBOOK) && defined(CONFIG_USB_ULPI)
-#include <usb/ulpi.h>
+#include <linux/usb/ulpi.h>
/*
* Add support for setting CHRGVBUS to workaround a hardware bug on efika mx/sb
* boards.
@@ -832,7 +869,7 @@ static int ehci_init(struct usb_host *host)
return ret;
}
- ehci->qh_list[0].qh_link = cpu_to_hc32((uint32_t)&ehci->qh_list[1] |
+ ehci->qh_list[0].qh_link = cpu_to_hc32(ehci_qh_dma(ehci, &ehci->qh_list[1]) |
QH_LINK_TYPE_QH);
ehci->qh_list[0].qh_endpt1 = cpu_to_hc32(QH_ENDPT1_H(1) |
QH_ENDPT1_EPS(USB_SPEED_HIGH));
@@ -841,12 +878,13 @@ static int ehci_init(struct usb_host *host)
ehci->qh_list[0].qt_altnext = cpu_to_hc32(QT_NEXT_TERMINATE);
ehci->qh_list[0].qt_token = cpu_to_hc32(QT_TOKEN_STATUS_HALTED);
- ehci->qh_list[1].qh_link = cpu_to_hc32((uint32_t)&ehci->qh_list[0] |
+ ehci->qh_list[1].qh_link = cpu_to_hc32(ehci_qh_dma(ehci,
+ &ehci->qh_list[0]) |
QH_LINK_TYPE_QH);
ehci->qh_list[1].qt_altnext = cpu_to_hc32(QT_NEXT_TERMINATE);
/* Set async. queue head pointer. */
- ehci_writel(&ehci->hcor->or_asynclistaddr, (uint32_t)ehci->qh_list);
+ ehci_writel(&ehci->hcor->or_asynclistaddr, (uint32_t)ehci->qh_list_dma);
/*
* Set up periodic list
@@ -854,7 +892,7 @@ static int ehci_init(struct usb_host *host)
*/
ehci->periodic_schedules = 0;
periodic = ehci->periodic_queue;
- memset(periodic, 0, sizeof(*periodic));
+ memzero32(periodic, sizeof(*periodic));
periodic->qh_link = cpu_to_hc32(QH_LINK_TERMINATE);
periodic->qt_next = cpu_to_hc32(QT_NEXT_TERMINATE);
periodic->qt_altnext = cpu_to_hc32(QT_NEXT_TERMINATE);
@@ -876,15 +914,15 @@ static int ehci_init(struct usb_host *host)
* PAGE_SIZE less then 4k will break this code.
*/
ehci->periodic_list = dma_alloc_coherent(1024 * 4,
- DMA_ADDRESS_BROKEN);
+ &ehci->periodic_list_dma);
for (i = 0; i < 1024; i++) {
- ehci->periodic_list[i] = cpu_to_hc32((unsigned long)periodic
+ ehci->periodic_list[i] = cpu_to_hc32((unsigned long)ehci->periodic_queue_dma
| QH_LINK_TYPE_QH);
}
/* Set periodic list base address */
ehci_writel(&ehci->hcor->or_periodiclistbase,
- (unsigned long)ehci->periodic_list);
+ (uint32_t)ehci->periodic_list_dma);
reg = ehci_readl(&ehci->hccr->cr_hcsparams);
descriptor.hub.bNbrPorts = HCS_N_PORTS(reg);
@@ -977,7 +1015,10 @@ disable_periodic(struct ehci_host *ehci)
return 0;
}
-#define NEXT_QH(qh) (struct QH *)((unsigned long)hc32_to_cpu((qh)->qh_link) & ~0x1f)
+#define NEXT_QH(queue, qh) (struct QH *)( \
+ ((unsigned long)hc32_to_cpu((qh)->qh_link) & ~0x1f) - \
+ (queue)->first_dma + \
+ (unsigned long)(queue)->first)
static int
enable_periodic(struct ehci_host *ehci)
@@ -1042,7 +1083,7 @@ static void ehci_update_endpt2_dev_n_port(struct usb_device *udev,
static struct int_queue *ehci_create_int_queue(struct usb_device *dev,
unsigned long pipe, int queuesize, int elementsize,
- void *buffer, int interval)
+ void *buffer, dma_addr_t buffer_dma, int interval)
{
struct usb_host *host = dev->host;
struct ehci_host *ehci = to_ehci(host);
@@ -1091,22 +1132,23 @@ static struct int_queue *ehci_create_int_queue(struct usb_device *dev,
result->queuesize = queuesize;
result->pipe = pipe;
result->first = dma_alloc_coherent(sizeof(struct QH) * queuesize,
- DMA_ADDRESS_BROKEN);
+ &result->first_dma);
result->current = result->first;
result->last = result->first + queuesize - 1;
result->tds = dma_alloc_coherent(sizeof(struct qTD) * queuesize,
- DMA_ADDRESS_BROKEN);
+ &result->tds_dma);
for (i = 0; i < queuesize; i++) {
struct QH *qh = result->first + i;
struct qTD *td = result->tds + i;
void **buf = &qh->buffer;
- qh->qh_link = cpu_to_hc32((unsigned long)(qh+1) | QH_LINK_TYPE_QH);
+ qh->qh_link = cpu_to_hc32(ehci_int_qh_dma(result, qh + 1) |
+ QH_LINK_TYPE_QH);
if (i == queuesize - 1)
qh->qh_link = cpu_to_hc32(QH_LINK_TERMINATE);
- qh->qt_next = cpu_to_hc32((unsigned long)td);
+ qh->qt_next = cpu_to_hc32(ehci_int_td_dma(result, td));
qh->qt_altnext = cpu_to_hc32(QT_NEXT_TERMINATE);
qh->qh_endpt1 =
cpu_to_hc32((0 << 28) | /* No NAK reload (ehci 4.9) */
@@ -1133,15 +1175,15 @@ static struct int_queue *ehci_create_int_queue(struct usb_device *dev,
((usb_pipein(pipe) ? 1 : 0) << 8) | /* IN/OUT token */
0x80); /* active */
td->qt_buffer[0] =
- cpu_to_hc32((unsigned long)buffer + i * elementsize);
+ cpu_to_hc32(buffer_dma + i * elementsize);
td->qt_buffer[1] =
- cpu_to_hc32((td->qt_buffer[0] + 0x1000) & ~0xfff);
+ cpu_to_hc32((buffer_dma + i * elementsize + 0x1000) & ~0xfff);
td->qt_buffer[2] =
- cpu_to_hc32((td->qt_buffer[0] + 0x2000) & ~0xfff);
+ cpu_to_hc32((buffer_dma + i * elementsize + 0x2000) & ~0xfff);
td->qt_buffer[3] =
- cpu_to_hc32((td->qt_buffer[0] + 0x3000) & ~0xfff);
+ cpu_to_hc32((buffer_dma + i * elementsize + 0x3000) & ~0xfff);
td->qt_buffer[4] =
- cpu_to_hc32((td->qt_buffer[0] + 0x4000) & ~0xfff);
+ cpu_to_hc32((buffer_dma + i * elementsize + 0x4000) & ~0xfff);
*buf = buffer + i * elementsize;
}
@@ -1156,7 +1198,7 @@ static struct int_queue *ehci_create_int_queue(struct usb_device *dev,
/* hook up to periodic list */
result->last->qh_link = list->qh_link;
- list->qh_link = cpu_to_hc32((unsigned long)result->first | QH_LINK_TYPE_QH);
+ list->qh_link = cpu_to_hc32(result->first_dma | QH_LINK_TYPE_QH);
if (enable_periodic(ehci) < 0) {
dev_err(&dev->dev,
@@ -1168,8 +1210,10 @@ static struct int_queue *ehci_create_int_queue(struct usb_device *dev,
dev_dbg(&dev->dev, "Exit create_int_queue\n");
return result;
fail3:
- dma_free_coherent(result->tds, 0, sizeof(struct qTD) * queuesize);
- dma_free_coherent(result->first, 0, sizeof(struct QH) * queuesize);
+ dma_free_coherent(result->tds, result->tds_dma,
+ sizeof(struct qTD) * queuesize);
+ dma_free_coherent(result->first, result->first_dma,
+ sizeof(struct QH) * queuesize);
free(result);
return NULL;
}
@@ -1226,14 +1270,14 @@ static int ehci_destroy_int_queue(struct usb_device *dev,
dev_dbg(&dev->dev,
"considering %p, with qh_link %x\n",
cur, cur->qh_link);
- if (NEXT_QH(cur) == queue->first) {
+ if (NEXT_QH(queue, cur) == queue->first) {
dev_dbg(&dev->dev,
"found candidate. removing from chain\n");
cur->qh_link = queue->last->qh_link;
result = 0;
break;
}
- cur = NEXT_QH(cur);
+ cur = NEXT_QH(queue, cur);
}
if (ehci->periodic_schedules > 0) {
@@ -1260,14 +1304,14 @@ submit_int_msg(struct usb_device *dev, unsigned long pipe, void *buffer,
uint64_t start;
void *backbuffer;
int result = 0, ret;
+ dma_addr_t buffer_dma;
dev_dbg(ehci->dev, "dev=%p, pipe=%lu, buffer=%p, length=%d, interval=%d",
dev, pipe, buffer, length, interval);
- dma_sync_single_for_device((unsigned long)buffer, length,
- DMA_BIDIRECTIONAL);
+ buffer_dma = dma_map_single(ehci->dev, buffer, length, DMA_BIDIRECTIONAL);
- queue = ehci_create_int_queue(dev, pipe, 1, length, buffer, interval);
+ queue = ehci_create_int_queue(dev, pipe, 1, length, buffer, buffer_dma, interval);
if (!queue)
return -EINVAL;
@@ -1289,8 +1333,7 @@ submit_int_msg(struct usb_device *dev, unsigned long pipe, void *buffer,
result = -EINVAL;
}
- dma_sync_single_for_cpu((unsigned long)buffer, length,
- DMA_BIDIRECTIONAL);
+ dma_unmap_single(ehci->dev, buffer_dma, length, DMA_BIDIRECTIONAL);
ret = ehci_destroy_int_queue(dev, queue);
if (!result)
@@ -1298,12 +1341,7 @@ submit_int_msg(struct usb_device *dev, unsigned long pipe, void *buffer,
return result;
}
-int ehci_detect(struct ehci_host *ehci)
-{
- return usb_host_detect(&ehci->host);
-}
-
-struct ehci_host *ehci_register(struct device_d *dev, struct ehci_data *data)
+struct ehci_host *ehci_register(struct device *dev, struct ehci_data *data)
{
struct usb_host *host;
struct ehci_host *ehci;
@@ -1326,11 +1364,11 @@ struct ehci_host *ehci_register(struct device_d *dev, struct ehci_data *data)
ehci->post_init = data->post_init;
ehci->qh_list = dma_alloc_coherent(sizeof(struct QH) * NUM_QH,
- DMA_ADDRESS_BROKEN);
+ &ehci->qh_list_dma);
ehci->periodic_queue = dma_alloc_coherent(sizeof(struct QH),
- DMA_ADDRESS_BROKEN);
+ &ehci->periodic_queue_dma);
ehci->td = dma_alloc_coherent(sizeof(struct qTD) * NUM_TD,
- DMA_ADDRESS_BROKEN);
+ &ehci->td_dma);
host->hw_dev = dev;
host->init = ehci_init;
@@ -1360,20 +1398,16 @@ void ehci_unregister(struct ehci_host *ehci)
free(ehci);
}
-static int ehci_dev_detect(struct device_d *dev)
-{
- struct ehci_host *ehci = dev->priv;
-
- return ehci_detect(ehci);
-}
-
-static int ehci_probe(struct device_d *dev)
+static int ehci_probe(struct device *dev)
{
struct resource *iores;
struct ehci_data data = {};
struct ehci_platform_data *pdata = dev->platform_data;
- struct device_node *dn = dev->device_node;
+ struct device_node *dn = dev->of_node;
struct ehci_host *ehci;
+ struct clk_bulk_data *clks;
+ int num_clocks, ret;
+ struct phy *usb2_generic_phy;
if (pdata)
data.flags = pdata->flags;
@@ -1387,6 +1421,27 @@ static int ehci_probe(struct device_d *dev)
*/
data.flags = EHCI_HAS_TT;
+ usb2_generic_phy = phy_optional_get(dev, "usb");
+ if (IS_ERR(usb2_generic_phy))
+ return PTR_ERR(usb2_generic_phy);
+
+ ret = phy_init(usb2_generic_phy);
+ if (ret)
+ return ret;
+
+ ret = phy_power_on(usb2_generic_phy);
+ if (ret)
+ return ret;
+
+ ret = clk_bulk_get_all(dev, &clks);
+ if (ret < 0)
+ return ret;
+
+ num_clocks = ret;
+ ret = clk_bulk_enable(num_clocks, clks);
+ if (ret)
+ return ret;
+
iores = dev_request_mem_resource(dev, 0);
if (IS_ERR(iores))
return PTR_ERR(iores);
@@ -1406,12 +1461,11 @@ static int ehci_probe(struct device_d *dev)
return PTR_ERR(ehci);
dev->priv = ehci;
- dev->detect = ehci_dev_detect;
return 0;
}
-static void ehci_remove(struct device_d *dev)
+static void ehci_remove(struct device *dev)
{
struct ehci_host *ehci = dev->priv;
@@ -1425,8 +1479,9 @@ static __maybe_unused struct of_device_id ehci_platform_dt_ids[] = {
/* sentinel */
}
};
+MODULE_DEVICE_TABLE(of, ehci_platform_dt_ids);
-static struct driver_d ehci_driver = {
+static struct driver ehci_driver = {
.name = "ehci",
.probe = ehci_probe,
.remove = ehci_remove,