source: mainline/uspace/drv/bus/usb/uhci/uhci.c@ ffa96c2

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since ffa96c2 was b5111c46, checked in by Jiri Svoboda <jiri@…>, 11 years ago

Convert OHCI and UHCI away from DDF_DATA_IMPLANT.

  • Property mode set to 100644
File size: 7.6 KB
RevLine 
[9351353]1/*
2 * Copyright (c) 2011 Jan Vesely
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * - Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * - Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * - The name of the author may not be used to endorse or promote products
15 * derived from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29/** @addtogroup drvusbuhci
30 * @{
31 */
32/** @file
33 * @brief UHCI driver
34 */
[56fd7cf]35
[9351353]36#include <errno.h>
[c53007f]37#include <stdbool.h>
[9351353]38#include <str_error.h>
39#include <ddf/interrupt.h>
40#include <usb_iface.h>
41#include <usb/ddfiface.h>
42#include <usb/debug.h>
43
44#include "uhci.h"
45
[795448f]46#include "res.h"
[d2bff2f]47#include "hc.h"
48#include "root_hub.h"
49
50/** Structure representing both functions of UHCI hc, USB host controller
51 * and USB root hub */
52typedef struct uhci {
[795448f]53 /** Pointer to DDF representation of UHCI host controller */
[d2bff2f]54 ddf_fun_t *hc_fun;
[795448f]55 /** Pointer to DDF representation of UHCI root hub */
[d2bff2f]56 ddf_fun_t *rh_fun;
57
[795448f]58 /** Internal driver's representation of UHCI host controller */
[d2bff2f]59 hc_t hc;
[795448f]60 /** Internal driver's representation of UHCI root hub */
[b5111c46]61 rh_t *rh;
[d2bff2f]62} uhci_t;
63
[56fd7cf]64static inline uhci_t *dev_to_uhci(ddf_dev_t *dev)
[d2bff2f]65{
[56fd7cf]66 return ddf_dev_data_get(dev);
[d2bff2f]67}
[76fbd9a]68
[9d2d444]69/** IRQ handling callback, forward status from call to diver structure.
[9351353]70 *
71 * @param[in] dev DDF instance of the device to use.
72 * @param[in] iid (Unused).
[9d2d444]73 * @param[in] call Pointer to the call from kernel.
[9351353]74 */
75static void irq_handler(ddf_dev_t *dev, ipc_callid_t iid, ipc_call_t *call)
76{
77 assert(dev);
[2df648c2]78 uhci_t *uhci = dev_to_uhci(dev);
[dfe4955]79 if (!uhci) {
80 usb_log_error("Interrupt on not yet initialized device.\n");
81 return;
82 }
[2df648c2]83 const uint16_t status = IPC_GET_ARG1(*call);
[fc6e607]84 hc_interrupt(&uhci->hc, status);
[9351353]85}
[76fbd9a]86
[2df648c2]87/** Operations supported by the HC driver */
88static ddf_dev_ops_t hc_ops = {
[5fe0a697]89 .interfaces[USBHC_DEV_IFACE] = &hcd_iface, /* see iface.h/c */
[2df648c2]90};
[76fbd9a]91
[9d2d444]92/** Gets handle of the respective hc.
[9351353]93 *
[9d2d444]94 * @param[in] fun DDF function of uhci device.
[ea993d18]95 * @param[out] handle Host cotnroller handle.
[9351353]96 * @return Error code.
97 */
[2df648c2]98static int usb_iface_get_hc_handle(ddf_fun_t *fun, devman_handle_t *handle)
[9351353]99{
[56fd7cf]100 ddf_fun_t *hc_fun = dev_to_uhci(ddf_fun_get_dev(fun))->hc_fun;
[d2bff2f]101 assert(hc_fun);
[9351353]102
[d2bff2f]103 if (handle != NULL)
[56fd7cf]104 *handle = ddf_fun_get_handle(hc_fun);
[9351353]105 return EOK;
106}
[76fbd9a]107
[9d2d444]108/** USB interface implementation used by RH */
[9351353]109static usb_iface_t usb_iface = {
110 .get_hc_handle = usb_iface_get_hc_handle,
111};
[76fbd9a]112
[17ceb72]113/** Get root hub hw resources (I/O registers).
[9351353]114 *
115 * @param[in] fun Root hub function.
116 * @return Pointer to the resource list used by the root hub.
117 */
118static hw_resource_list_t *get_resource_list(ddf_fun_t *fun)
119{
[56fd7cf]120 rh_t *rh = ddf_fun_data_get(fun);
[e247d83]121 assert(rh);
122 return &rh->resource_list;
[9351353]123}
[76fbd9a]124
[9d2d444]125/** Interface to provide the root hub driver with hw info */
[9351353]126static hw_res_ops_t hw_res_iface = {
127 .get_resource_list = get_resource_list,
[d2bff2f]128 .enable_interrupt = NULL,
[9351353]129};
[76fbd9a]130
[5b89d43b]131static pio_window_t *get_pio_window(ddf_fun_t *fun)
132{
133 rh_t *rh = ddf_fun_data_get(fun);
134
135 if (rh == NULL)
136 return NULL;
137 return &rh->pio_window;
138}
139
140static pio_window_ops_t pio_window_iface = {
141 .get_pio_window = get_pio_window
142};
143
[8d6c1f1]144/** RH function support for uhci_rhd */
[33fbe95]145static ddf_dev_ops_t rh_ops = {
[9351353]146 .interfaces[USB_DEV_IFACE] = &usb_iface,
[5b89d43b]147 .interfaces[HW_RES_DEV_IFACE] = &hw_res_iface,
148 .interfaces[PIO_WINDOW_DEV_IFACE] = &pio_window_iface
[9351353]149};
[76fbd9a]150
[9d2d444]151/** Initialize hc and rh DDF structures and their respective drivers.
[17ceb72]152 *
153 * @param[in] device DDF instance of the device to use.
154 *
155 * This function does all the preparatory work for hc and rh drivers:
[9d2d444]156 * - gets device's hw resources
157 * - disables UHCI legacy support (PCI config space)
[23f40280]158 * - attempts to enable interrupts
[17ceb72]159 * - registers interrupt handler
160 */
[d2bff2f]161int device_setup_uhci(ddf_dev_t *device)
[9351353]162{
[c53007f]163 bool ih_registered = false;
164 bool hc_inited = false;
165 bool fun_bound = false;
166 int rc;
167
[1664a26]168 if (!device)
169 return EBADMEM;
170
171 uhci_t *instance = ddf_dev_data_alloc(device, sizeof(uhci_t));
[d2bff2f]172 if (instance == NULL) {
173 usb_log_error("Failed to allocate OHCI driver.\n");
174 return ENOMEM;
175 }
176
[8d6c1f1]177 instance->hc_fun = ddf_fun_create(device, fun_exposed, "uhci_hc");
[c53007f]178 if (instance->hc_fun == NULL) {
179 usb_log_error("Failed to create UHCI HC function.\n");
180 rc = ENOMEM;
181 goto error;
182 }
183
[56fd7cf]184 ddf_fun_set_ops(instance->hc_fun, &hc_ops);
[d2bff2f]185
[8d6c1f1]186 instance->rh_fun = ddf_fun_create(device, fun_inner, "uhci_rh");
[c53007f]187 if (instance->rh_fun == NULL) {
188 usb_log_error("Failed to create UHCI RH function.\n");
189 rc = ENOMEM;
190 goto error;
191 }
192
[56fd7cf]193 ddf_fun_set_ops(instance->rh_fun, &rh_ops);
[b5111c46]194 instance->rh = ddf_fun_data_alloc(instance->rh_fun, sizeof(rh_t));
[9351353]195
[7de1988c]196 addr_range_t regs;
[9351353]197 int irq = 0;
198
[7de1988c]199 rc = get_my_registers(device, &regs, &irq);
[c53007f]200 if (rc != EOK) {
201 usb_log_error("Failed to get I/O addresses for %" PRIun ": %s.\n",
202 ddf_dev_get_handle(device), str_error(rc));
203 goto error;
204 }
[7de1988c]205 usb_log_debug("I/O regs at %p (size %zu), IRQ %d.\n",
206 RNGABSPTR(regs), RNGSZ(regs), irq);
[9351353]207
[c53007f]208 rc = disable_legacy(device);
209 if (rc != EOK) {
210 usb_log_error("Failed to disable legacy USB: %s.\n",
211 str_error(rc));
212 goto error;
213 }
214
[7de1988c]215 rc = hc_register_irq_handler(device, &regs, irq, irq_handler);
[c53007f]216 if (rc != EOK) {
217 usb_log_error("Failed to register interrupt handler: %s.\n",
218 str_error(rc));
219 goto error;
220 }
221
222 ih_registered = true;
[9351353]223
[ff34e5a]224 bool interrupts = false;
[c53007f]225 rc = enable_interrupts(device);
226 if (rc != EOK) {
[dfe4955]227 usb_log_warning("Failed to enable interrupts: %s."
[c53007f]228 " Falling back to polling.\n", str_error(rc));
[ff34e5a]229 } else {
230 usb_log_debug("Hw interrupts enabled.\n");
231 interrupts = true;
[9351353]232 }
233
[b5111c46]234 rc = hc_init(&instance->hc, instance->hc_fun, &regs, interrupts);
[c53007f]235 if (rc != EOK) {
236 usb_log_error("Failed to init uhci_hcd: %s.\n", str_error(rc));
237 goto error;
238 }
[9d2d444]239
[c53007f]240 hc_inited = true;
[9351353]241
[c53007f]242 rc = ddf_fun_bind(instance->hc_fun);
243 if (rc != EOK) {
244 usb_log_error("Failed to bind UHCI device function: %s.\n",
245 str_error(rc));
246 goto error;
247 }
[9351353]248
[c53007f]249 fun_bound = true;
250
251 rc = ddf_fun_add_to_category(instance->hc_fun, USB_HC_CATEGORY);
252 if (rc != EOK) {
253 usb_log_error("Failed to add UHCI to HC class: %s.\n",
254 str_error(rc));
255 goto error;
256 }
[76ef94e]257
[b5111c46]258 rc = rh_init(instance->rh, instance->rh_fun, &regs, 0x10, 4);
[c53007f]259 if (rc != EOK) {
260 usb_log_error("Failed to setup UHCI root hub: %s.\n",
261 str_error(rc));
262 goto error;
263 }
[9351353]264
[c53007f]265 rc = ddf_fun_bind(instance->rh_fun);
266 if (rc != EOK) {
267 usb_log_error("Failed to register UHCI root hub: %s.\n",
268 str_error(rc));
269 goto error;
270 }
[9351353]271
272 return EOK;
[c53007f]273
274error:
275 if (fun_bound)
276 ddf_fun_unbind(instance->hc_fun);
277 if (hc_inited)
278 hc_fini(&instance->hc);
279 if (ih_registered)
280 unregister_interrupt_handler(device, irq);
281 if (instance->hc_fun != NULL)
282 ddf_fun_destroy(instance->hc_fun);
283 if (instance->rh_fun != NULL) {
284 ddf_fun_destroy(instance->rh_fun);
285 }
286 return rc;
[9351353]287}
288/**
289 * @}
290 */
Note: See TracBrowser for help on using the repository browser.