diff options
author | Alexander Shishkin <alexander.shishkin@linux.intel.com> | 2012-05-11 10:25:54 -0400 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2012-05-11 19:56:15 -0400 |
commit | eb70e5ab8f95a81283623c03d2c99dfc59fcb319 (patch) | |
tree | fc20c9d10484171490cf6b69f7f5b6f934803366 /drivers/usb/chipidea/host.c | |
parent | 758fc9860c19eceb56e5886a5225db623c521971 (diff) |
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>
Diffstat (limited to 'drivers/usb/chipidea/host.c')
-rw-r--r-- | drivers/usb/chipidea/host.c | 158 |
1 files changed, 158 insertions, 0 deletions
diff --git a/drivers/usb/chipidea/host.c b/drivers/usb/chipidea/host.c new file mode 100644 index 000000000000..8c8362c89a8c --- /dev/null +++ b/drivers/usb/chipidea/host.c | |||
@@ -0,0 +1,158 @@ | |||
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 | } | ||