Commit eb70e5ab8f95a81283623c03d2c99dfc59fcb319

Authored by Alexander Shishkin
Committed by Greg Kroah-Hartman
1 parent 758fc9860c

usb: chipidea: add host role

This adds EHCI host support to the chipidea driver. We want it to be
part of the hdrc driver and not a standalone (sub-)driver module, as
the structure of ehci-hcd.c suggests, so for chipidea controller we
hack it to not provide platform-related code, but only the ehci hcd.

The ehci-platform driver won't work for us here too, because the
controller uses the same registers for both device and host mode and
also otg-related bits, so it's not really possible to put ehci registers
into a separate resource.

This is not a pretty solution, but the alternative is exporting symbols
from the chipidea driver to a ehci-chipidea driver and doing all the
module refcounting.

Signed-off-by: Alexander Shishkin <alexander.shishkin@linux.intel.com>
Cc: Alan Stern <stern@rowland.harvard.edu>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>

Showing 9 changed files with 212 additions and 9 deletions Side-by-side Diff

drivers/usb/chipidea/Kconfig
... ... @@ -18,6 +18,12 @@
18 18 Say Y here to enable device controller functionality of the
19 19 ChipIdea driver.
20 20  
  21 +config USB_CHIPIDEA_HOST
  22 + bool "ChipIdea host controller"
  23 + help
  24 + Say Y here to enable host controller functionality of the
  25 + ChipIdea driver.
  26 +
21 27 config USB_CHIPIDEA_DEBUG
22 28 bool "ChipIdea driver debug"
23 29 help
drivers/usb/chipidea/Makefile
... ... @@ -2,6 +2,7 @@
2 2  
3 3 ci_hdrc-y := core.o
4 4 ci_hdrc-$(CONFIG_USB_CHIPIDEA_UDC) += udc.o
  5 +ci_hdrc-$(CONFIG_USB_CHIPIDEA_HOST) += host.o
5 6 ci_hdrc-$(CONFIG_USB_CHIPIDEA_DEBUG) += debug.o
6 7  
7 8 ifneq ($(CONFIG_PCI),)
drivers/usb/chipidea/bits.h
... ... @@ -21,6 +21,7 @@
21 21 /* DCCPARAMS */
22 22 #define DCCPARAMS_DEN (0x1F << 0)
23 23 #define DCCPARAMS_DC BIT(7)
  24 +#define DCCPARAMS_HC BIT(8)
24 25  
25 26 /* TESTMODE */
26 27 #define TESTMODE_FORCE BIT(0)
drivers/usb/chipidea/ci.h
... ... @@ -15,6 +15,7 @@
15 15  
16 16 #include <linux/list.h>
17 17 #include <linux/irqreturn.h>
  18 +#include <linux/usb.h>
18 19 #include <linux/usb/gadget.h>
19 20  
20 21 /******************************************************************************
... ... @@ -84,6 +85,7 @@
84 85 /**
85 86 * struct hw_bank - hardware register mapping representation
86 87 * @lpm: set if the device is LPM capable
  88 + * @phys: physical address of the controller's registers
87 89 * @abs: absolute address of the beginning of register window
88 90 * @cap: capability registers
89 91 * @op: operational registers
... ... @@ -92,6 +94,7 @@
92 94 */
93 95 struct hw_bank {
94 96 unsigned lpm;
  97 + resource_size_t phys;
95 98 void __iomem *abs;
96 99 void __iomem *cap;
97 100 void __iomem *op;
... ... @@ -128,6 +131,7 @@
128 131 * @udc_driver: platform specific information supplied by parent device
129 132 * @vbus_active: is VBUS active
130 133 * @transceiver: pointer to USB PHY, if any
  134 + * @hcd: pointer to usb_hcd for ehci host driver
131 135 */
132 136 struct ci13xxx {
133 137 struct device *dev;
... ... @@ -160,6 +164,7 @@
160 164 struct ci13xxx_udc_driver *udc_driver;
161 165 int vbus_active;
162 166 struct usb_phy *transceiver;
  167 + struct usb_hcd *hcd;
163 168 };
164 169  
165 170 static inline struct ci_role_driver *ci_role(struct ci13xxx *ci)
... ... @@ -302,7 +307,7 @@
302 307 return (val & mask) >> ffs_nr(mask);
303 308 }
304 309  
305   -int hw_device_reset(struct ci13xxx *ci);
  310 +int hw_device_reset(struct ci13xxx *ci, u32 mode);
306 311  
307 312 int hw_port_test_set(struct ci13xxx *ci, u8 mode);
308 313  
drivers/usb/chipidea/core.c
... ... @@ -70,6 +70,7 @@
70 70 #include "ci.h"
71 71 #include "udc.h"
72 72 #include "bits.h"
  73 +#include "host.h"
73 74 #include "debug.h"
74 75  
75 76 /* Controller register map */
... ... @@ -215,7 +216,7 @@
215 216 *
216 217 * This function returns an error code
217 218 */
218   -int hw_device_reset(struct ci13xxx *ci)
  219 +int hw_device_reset(struct ci13xxx *ci, u32 mode)
219 220 {
220 221 /* should flush & stop before reset */
221 222 hw_write(ci, OP_ENDPTFLUSH, ~0, ~0);
222 223  
... ... @@ -235,12 +236,12 @@
235 236  
236 237 /* USBMODE should be configured step by step */
237 238 hw_write(ci, OP_USBMODE, USBMODE_CM, USBMODE_CM_IDLE);
238   - hw_write(ci, OP_USBMODE, USBMODE_CM, USBMODE_CM_DC);
  239 + hw_write(ci, OP_USBMODE, USBMODE_CM, mode);
239 240 /* HW >= 2.3 */
240 241 hw_write(ci, OP_USBMODE, USBMODE_SLOM, USBMODE_SLOM);
241 242  
242   - if (hw_read(ci, OP_USBMODE, USBMODE_CM) != USBMODE_CM_DC) {
243   - pr_err("cannot enter in device mode");
  243 + if (hw_read(ci, OP_USBMODE, USBMODE_CM) != mode) {
  244 + pr_err("cannot enter in %s mode", ci_role(ci)->name);
244 245 pr_err("lpm = %i", ci->hw_bank.lpm);
245 246 return -ENODEV;
246 247 }
... ... @@ -371,6 +372,8 @@
371 372 return -ENODEV;
372 373 }
373 374  
  375 + ci->hw_bank.phys = res->start;
  376 +
374 377 ci->irq = platform_get_irq(pdev, 0);
375 378 if (ci->irq < 0) {
376 379 dev_err(dev, "missing IRQ\n");
... ... @@ -385,6 +388,10 @@
385 388 }
386 389  
387 390 /* initialize role(s) before the interrupt is requested */
  391 + ret = ci_hdrc_host_init(ci);
  392 + if (ret)
  393 + dev_info(dev, "doesn't support host\n");
  394 +
388 395 ret = ci_hdrc_gadget_init(ci);
389 396 if (ret)
390 397 dev_info(dev, "doesn't support gadget\n");
drivers/usb/chipidea/host.c
  1 +/*
  2 + * host.c - ChipIdea USB host controller driver
  3 + *
  4 + * Copyright (c) 2012 Intel Corporation
  5 + *
  6 + * Author: Alexander Shishkin
  7 + *
  8 + * This program is free software; you can redistribute it and/or modify
  9 + * it under the terms of the GNU General Public License version 2 as
  10 + * published by the Free Software Foundation.
  11 + *
  12 + * This program is distributed in the hope that it will be useful,
  13 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  15 + * GNU General Public License for more details.
  16 + *
  17 + * You should have received a copy of the GNU General Public License
  18 + * along with this program; if not, write to the Free Software
  19 + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  20 + */
  21 +
  22 +#include <linux/kernel.h>
  23 +#include <linux/usb.h>
  24 +#include <linux/usb/hcd.h>
  25 +#include <linux/usb/chipidea.h>
  26 +
  27 +#define CHIPIDEA_EHCI
  28 +#include "../host/ehci-hcd.c"
  29 +
  30 +#include "ci.h"
  31 +#include "bits.h"
  32 +#include "host.h"
  33 +
  34 +static int ci_ehci_setup(struct usb_hcd *hcd)
  35 +{
  36 + struct ehci_hcd *ehci = hcd_to_ehci(hcd);
  37 + int ret;
  38 +
  39 + hcd->has_tt = 1;
  40 +
  41 + ret = ehci_setup(hcd);
  42 + if (ret)
  43 + return ret;
  44 +
  45 + ehci_port_power(ehci, 0);
  46 +
  47 + return ret;
  48 +}
  49 +
  50 +static const struct hc_driver ci_ehci_hc_driver = {
  51 + .description = "ehci_hcd",
  52 + .product_desc = "ChipIdea HDRC EHCI",
  53 + .hcd_priv_size = sizeof(struct ehci_hcd),
  54 +
  55 + /*
  56 + * generic hardware linkage
  57 + */
  58 + .irq = ehci_irq,
  59 + .flags = HCD_MEMORY | HCD_USB2,
  60 +
  61 + /*
  62 + * basic lifecycle operations
  63 + */
  64 + .reset = ci_ehci_setup,
  65 + .start = ehci_run,
  66 + .stop = ehci_stop,
  67 + .shutdown = ehci_shutdown,
  68 +
  69 + /*
  70 + * managing i/o requests and associated device resources
  71 + */
  72 + .urb_enqueue = ehci_urb_enqueue,
  73 + .urb_dequeue = ehci_urb_dequeue,
  74 + .endpoint_disable = ehci_endpoint_disable,
  75 + .endpoint_reset = ehci_endpoint_reset,
  76 +
  77 + /*
  78 + * scheduling support
  79 + */
  80 + .get_frame_number = ehci_get_frame,
  81 +
  82 + /*
  83 + * root hub support
  84 + */
  85 + .hub_status_data = ehci_hub_status_data,
  86 + .hub_control = ehci_hub_control,
  87 + .bus_suspend = ehci_bus_suspend,
  88 + .bus_resume = ehci_bus_resume,
  89 + .relinquish_port = ehci_relinquish_port,
  90 + .port_handed_over = ehci_port_handed_over,
  91 +
  92 + .clear_tt_buffer_complete = ehci_clear_tt_buffer_complete,
  93 +};
  94 +
  95 +static irqreturn_t host_irq(struct ci13xxx *ci)
  96 +{
  97 + return usb_hcd_irq(ci->irq, ci->hcd);
  98 +}
  99 +
  100 +static int host_start(struct ci13xxx *ci)
  101 +{
  102 + struct usb_hcd *hcd;
  103 + struct ehci_hcd *ehci;
  104 + int ret;
  105 +
  106 + if (usb_disabled())
  107 + return -ENODEV;
  108 +
  109 + hcd = usb_create_hcd(&ci_ehci_hc_driver, ci->dev, dev_name(ci->dev));
  110 + if (!hcd)
  111 + return -ENOMEM;
  112 +
  113 + dev_set_drvdata(ci->dev, ci);
  114 + hcd->rsrc_start = ci->hw_bank.phys;
  115 + hcd->rsrc_len = ci->hw_bank.size;
  116 + hcd->regs = ci->hw_bank.abs;
  117 + hcd->has_tt = 1;
  118 +
  119 + ehci = hcd_to_ehci(hcd);
  120 + ehci->caps = ci->hw_bank.cap;
  121 + ehci->has_hostpc = ci->hw_bank.lpm;
  122 +
  123 + ret = usb_add_hcd(hcd, 0, 0);
  124 + if (ret)
  125 + usb_remove_hcd(hcd);
  126 + else
  127 + ci->hcd = hcd;
  128 +
  129 + return ret;
  130 +}
  131 +
  132 +static void host_stop(struct ci13xxx *ci)
  133 +{
  134 + struct usb_hcd *hcd = ci->hcd;
  135 +
  136 + usb_remove_hcd(hcd);
  137 + usb_put_hcd(hcd);
  138 +}
  139 +
  140 +int ci_hdrc_host_init(struct ci13xxx *ci)
  141 +{
  142 + struct ci_role_driver *rdrv;
  143 +
  144 + if (!hw_read(ci, CAP_DCCPARAMS, DCCPARAMS_HC))
  145 + return -ENXIO;
  146 +
  147 + rdrv = devm_kzalloc(ci->dev, sizeof(struct ci_role_driver), GFP_KERNEL);
  148 + if (!rdrv)
  149 + return -ENOMEM;
  150 +
  151 + rdrv->start = host_start;
  152 + rdrv->stop = host_stop;
  153 + rdrv->irq = host_irq;
  154 + rdrv->name = "host";
  155 + ci->roles[CI_ROLE_HOST] = rdrv;
  156 +
  157 + return 0;
  158 +}
drivers/usb/chipidea/host.h
  1 +#ifndef __DRIVERS_USB_CHIPIDEA_HOST_H
  2 +#define __DRIVERS_USB_CHIPIDEA_HOST_H
  3 +
  4 +#ifdef CONFIG_USB_CHIPIDEA_HOST
  5 +
  6 +int ci_hdrc_host_init(struct ci13xxx *ci);
  7 +
  8 +#else
  9 +
  10 +static inline int ci_hdrc_host_init(struct ci13xxx *ci)
  11 +{
  12 + return -ENXIO;
  13 +}
  14 +
  15 +#endif
  16 +
  17 +#endif /* __DRIVERS_USB_CHIPIDEA_HOST_H */
drivers/usb/chipidea/udc.c
1 1 /*
2   - * udc.h - ChipIdea UDC driver
  2 + * udc.c - ChipIdea UDC driver
3 3 *
4 4 * Copyright (C) 2008 Chipidea - MIPS Technologies, Inc. All rights reserved.
5 5 *
... ... @@ -1396,7 +1396,7 @@
1396 1396 if (gadget_ready) {
1397 1397 if (is_active) {
1398 1398 pm_runtime_get_sync(&_gadget->dev);
1399   - hw_device_reset(udc);
  1399 + hw_device_reset(udc, USBMODE_CM_DC);
1400 1400 hw_device_state(udc, udc->ep0out->qh.dma);
1401 1401 } else {
1402 1402 hw_device_state(udc, 0);
... ... @@ -1540,7 +1540,7 @@
1540 1540 if (udc->udc_driver->flags & CI13XXX_PULLUP_ON_VBUS) {
1541 1541 if (udc->vbus_active) {
1542 1542 if (udc->udc_driver->flags & CI13XXX_REGS_SHARED)
1543   - hw_device_reset(udc);
  1543 + hw_device_reset(udc, USBMODE_CM_DC);
1544 1544 } else {
1545 1545 pm_runtime_put_sync(&udc->gadget.dev);
1546 1546 goto done;
... ... @@ -1720,7 +1720,7 @@
1720 1720 }
1721 1721  
1722 1722 if (!(udc->udc_driver->flags & CI13XXX_REGS_SHARED)) {
1723   - retval = hw_device_reset(udc);
  1723 + retval = hw_device_reset(udc, USBMODE_CM_DC);
1724 1724 if (retval)
1725 1725 goto put_transceiver;
1726 1726 }
drivers/usb/host/ehci-hcd.c
... ... @@ -1246,6 +1246,13 @@
1246 1246 }
1247 1247  
1248 1248 /*-------------------------------------------------------------------------*/
  1249 +/*
  1250 + * The EHCI in ChipIdea HDRC cannot be a separate module or device,
  1251 + * because its registers (and irq) are shared between host/gadget/otg
  1252 + * functions and in order to facilitate role switching we cannot
  1253 + * give the ehci driver exclusive access to those.
  1254 + */
  1255 +#ifndef CHIPIDEA_EHCI
1249 1256  
1250 1257 MODULE_DESCRIPTION(DRIVER_DESC);
1251 1258 MODULE_AUTHOR (DRIVER_AUTHOR);
... ... @@ -1503,4 +1510,6 @@
1503 1510 clear_bit(USB_EHCI_LOADED, &usb_hcds_loaded);
1504 1511 }
1505 1512 module_exit(ehci_hcd_cleanup);
  1513 +
  1514 +#endif /* CHIPIDEA_EHCI */