blob: eb252cd8adeba5e947a2c3f07dd8a23c3a25f2ea [file] [log] [blame]
Patrick Georgid21f68b2008-09-02 16:06:22 +00001/*
Patrick Georgid21f68b2008-09-02 16:06:22 +00002 *
Stefan Reinauerb56f2d02010-03-25 22:17:36 +00003 * Copyright (C) 2008-2010 coresystems GmbH
Patrick Georgid21f68b2008-09-02 16:06:22 +00004 *
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 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * 3. The name of the author may not be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26 * SUCH DAMAGE.
27 */
28
Stefan Reinauerb56f2d02010-03-25 22:17:36 +000029//#define USB_DEBUG
30
Patrick Georgid78691d2010-06-07 13:58:17 +000031#include <arch/virtual.h>
Caveh Jalali8079a6a2020-09-01 20:37:49 -070032#include <inttypes.h>
Jordan Crouse29061a52008-09-11 17:29:00 +000033#include <usb/usb.h>
Patrick Georgid21f68b2008-09-02 16:06:22 +000034#include "uhci.h"
Patrick Georgid78691d2010-06-07 13:58:17 +000035#include "uhci_private.h"
Patrick Georgid21f68b2008-09-02 16:06:22 +000036
Yidi Lin5ef258b2022-08-10 14:59:18 +080037static void uhci_start(hci_t *controller);
38static void uhci_stop(hci_t *controller);
39static void uhci_reset(hci_t *controller);
40static void uhci_shutdown(hci_t *controller);
41static int uhci_bulk(endpoint_t *ep, int size, u8 *data, int finalize);
42static int uhci_control(usbdev_t *dev, direction_t dir, int drlen, void *devreq,
Patrick Georgid21f68b2008-09-02 16:06:22 +000043 int dalen, u8 *data);
Yidi Lin5ef258b2022-08-10 14:59:18 +080044static void* uhci_create_intr_queue(endpoint_t *ep, int reqsize, int reqcount, int reqtiming);
45static void uhci_destroy_intr_queue(endpoint_t *ep, void *queue);
46static u8* uhci_poll_intr_queue(void *queue);
Patrick Georgid21f68b2008-09-02 16:06:22 +000047
48#if 0
49/* dump uhci */
50static void
Yidi Lin5ef258b2022-08-10 14:59:18 +080051uhci_dump(hci_t *controller)
Patrick Georgid21f68b2008-09-02 16:06:22 +000052{
Yidi Lin5ef258b2022-08-10 14:59:18 +080053 usb_debug("dump:\nUSBCMD: %x\n", uhci_reg_read16(controller, USBCMD));
54 usb_debug("USBSTS: %x\n", uhci_reg_read16(controller, USBSTS));
55 usb_debug("USBINTR: %x\n", uhci_reg_read16(controller, USBINTR));
56 usb_debug("FRNUM: %x\n", uhci_reg_read16(controller, FRNUM));
57 usb_debug("FLBASEADD: %x\n", uhci_reg_read32(controller, FLBASEADD));
58 usb_debug("SOFMOD: %x\n", uhci_reg_read8(controller, SOFMOD));
59 usb_debug("PORTSC1: %x\n", uhci_reg_read16(controller, PORTSC1));
60 usb_debug("PORTSC2: %x\n", uhci_reg_read16(controller, PORTSC2));
Patrick Georgid21f68b2008-09-02 16:06:22 +000061}
62#endif
63
Anton Kochkovc62b69c2012-12-19 13:49:20 +040064static void td_dump(td_t *td)
Patrick Georgid21f68b2008-09-02 16:06:22 +000065{
Anton Kochkovc62b69c2012-12-19 13:49:20 +040066 usb_debug("+---------------------------------------------------+\n");
67 if ((td->token & TD_PID_MASK) == UHCI_SETUP)
68 usb_debug("|..[SETUP]..........................................|\n");
69 else if ((td->token & TD_PID_MASK) == UHCI_IN)
70 usb_debug("|..[IN].............................................|\n");
71 else if ((td->token & TD_PID_MASK) == UHCI_OUT)
72 usb_debug("|..[OUT]............................................|\n");
73 else
74 usb_debug("|..[]...............................................|\n");
75 usb_debug("|:|============ UHCI TD at [0x%08lx] ==========|:|\n", virt_to_phys(td));
76 usb_debug("|:+-----------------------------------------------+:|\n");
77 usb_debug("|:| Next TD/QH [0x%08lx] |:|\n", td->ptr & ~0xFUL);
78 usb_debug("|:+-----------------------------------------------+:|\n");
79 usb_debug("|:| Depth/Breath [%lx] | QH/TD [%lx] | TERMINATE [%lx] |:|\n",
80 (td->ptr & (1UL << 2)) >> 2, (td->ptr & (1UL << 1)) >> 1, td->ptr & 1UL);
81 usb_debug("|:+-----------------------------------------------+:|\n");
82 usb_debug("|:| T | Maximum Length | [%04lx] |:|\n", (td->token & (0x7FFUL << 21)) >> 21);
Caveh Jalali8079a6a2020-09-01 20:37:49 -070083 usb_debug("|:| O | PID CODE | [%04"PRIx32"] |:|\n", td->token & 0xFF);
84 usb_debug("|:| K | Endpoint | [%04"PRIx32"] |:|\n", (td->token & TD_EP_MASK) >> TD_EP_SHIFT);
Anton Kochkovc62b69c2012-12-19 13:49:20 +040085 usb_debug("|:| E | Device Address | [%04lx] |:|\n", (td->token & (0x7FUL << 8)) >> 8);
86 usb_debug("|:| N | Data Toggle | [%lx] |:|\n", (td->token & (1UL << 19)) >> 19);
87 usb_debug("|:+-----------------------------------------------+:|\n");
88 usb_debug("|:| C | Short Packet Detector | [%lx] |:|\n", (td->ctrlsts & (1UL << 29)) >> 29);
89 usb_debug("|:| O | Error Counter | [%lx] |:|\n",
Caveh Jalali8079a6a2020-09-01 20:37:49 -070090 (td->ctrlsts & (3UL << TD_COUNTER_SHIFT)) >> TD_COUNTER_SHIFT);
Anton Kochkovc62b69c2012-12-19 13:49:20 +040091 usb_debug("|:| N | Low Speed Device | [%lx] |:|\n", (td->ctrlsts & (1UL << 26)) >> 26);
92 usb_debug("|:| T | Isochronous Select | [%lx] |:|\n", (td->ctrlsts & (1UL << 25)) >> 25);
93 usb_debug("|:| R | Interrupt on Complete (IOC) | [%lx] |:|\n", (td->ctrlsts & (1UL << 24)) >> 24);
94 usb_debug("|:+ O ----------------------------------------+:|\n");
95 usb_debug("|:| L | Active | [%lx] |:|\n", (td->ctrlsts & (1UL << 23)) >> 23);
96 usb_debug("|:| & | Stalled | [%lx] |:|\n", (td->ctrlsts & (1UL << 22)) >> 22);
97 usb_debug("|:| S | Data Buffer Error | [%lx] |:|\n", (td->ctrlsts & (1UL << 21)) >> 21);
98 usb_debug("|:| T | Bubble Detected | [%lx] |:|\n", (td->ctrlsts & (1UL << 20)) >> 20);
99 usb_debug("|:| A | NAK Received | [%lx] |:|\n", (td->ctrlsts & (1UL << 19)) >> 19);
100 usb_debug("|:| T | CRC/Timeout Error | [%lx] |:|\n", (td->ctrlsts & (1UL << 18)) >> 18);
101 usb_debug("|:| U | Bitstuff Error | [%lx] |:|\n", (td->ctrlsts & (1UL << 17)) >> 17);
102 usb_debug("|:| S ----------------------------------------|:|\n");
103 usb_debug("|:| | Actual Length | [%04lx] |:|\n", td->ctrlsts & 0x7FFUL);
104 usb_debug("|:+-----------------------------------------------+:|\n");
Caveh Jalali8079a6a2020-09-01 20:37:49 -0700105 usb_debug("|:| Buffer pointer [0x%08"PRIx32"] |:|\n", td->bufptr);
Anton Kochkovc62b69c2012-12-19 13:49:20 +0400106 usb_debug("|:|-----------------------------------------------|:|\n");
107 usb_debug("|...................................................|\n");
108 usb_debug("+---------------------------------------------------+\n");
Patrick Georgid21f68b2008-09-02 16:06:22 +0000109}
110
111static void
Yidi Lin5ef258b2022-08-10 14:59:18 +0800112uhci_reset(hci_t *controller)
Patrick Georgid21f68b2008-09-02 16:06:22 +0000113{
114 /* reset */
Yidi Lin5ef258b2022-08-10 14:59:18 +0800115 uhci_reg_write16(controller, USBCMD, 4); /* Global Reset */
116 mdelay(50); /* uhci spec 2.1.1: at least 10ms */
117 uhci_reg_write16(controller, USBCMD, 0);
118 mdelay(10);
119 uhci_reg_write16(controller, USBCMD, 2); /* Host Controller Reset */
Nico Huberbb1c42b2012-05-21 14:23:03 +0200120 /* wait for controller to finish reset */
121 /* TOTEST: how long to wait? 100ms for now */
122 int timeout = 200; /* time out after 200 * 500us == 100ms */
Yidi Lin5ef258b2022-08-10 14:59:18 +0800123 while (((uhci_reg_read16(controller, USBCMD) & 2) != 0) && timeout--)
124 udelay(500);
Nico Huberbb1c42b2012-05-21 14:23:03 +0200125 if (timeout < 0)
Yidi Lin5ef258b2022-08-10 14:59:18 +0800126 usb_debug("Warning: uhci: host controller reset timed out.\n");
Nico Huber6e711c62012-11-12 16:20:32 +0100127}
Patrick Georgid21f68b2008-09-02 16:06:22 +0000128
Nico Huber6e711c62012-11-12 16:20:32 +0100129static void
Yidi Lin5ef258b2022-08-10 14:59:18 +0800130uhci_reinit(hci_t *controller)
Nico Huber6e711c62012-11-12 16:20:32 +0100131{
Yidi Lin5ef258b2022-08-10 14:59:18 +0800132 uhci_reg_write32(controller, FLBASEADD,
133 (u32) virt_to_phys(UHCI_INST(controller)->
Patrick Georgid21f68b2008-09-02 16:06:22 +0000134 framelistptr));
Gabe Black93ded592012-11-01 15:44:10 -0700135 //usb_debug ("framelist at %p\n",UHCI_INST(controller)->framelistptr);
Patrick Georgid21f68b2008-09-02 16:06:22 +0000136
137 /* disable irqs */
Yidi Lin5ef258b2022-08-10 14:59:18 +0800138 uhci_reg_write16(controller, USBINTR, 0);
Patrick Georgid21f68b2008-09-02 16:06:22 +0000139
140 /* reset framelist index */
Yidi Lin5ef258b2022-08-10 14:59:18 +0800141 uhci_reg_write16(controller, FRNUM, 0);
Patrick Georgid21f68b2008-09-02 16:06:22 +0000142
Patrick Georgif42fdab2011-11-18 14:44:16 +0100143 uhci_reg_write16(controller, USBCMD,
144 uhci_reg_read16(controller, USBCMD) | 0xc0); // max packets, configure flag
Patrick Georgid21f68b2008-09-02 16:06:22 +0000145
Yidi Lin5ef258b2022-08-10 14:59:18 +0800146 uhci_start(controller);
Patrick Georgid21f68b2008-09-02 16:06:22 +0000147}
148
149hci_t *
Yidi Lin5ef258b2022-08-10 14:59:18 +0800150uhci_pci_init(pcidev_t addr)
Patrick Georgid21f68b2008-09-02 16:06:22 +0000151{
152 int i;
Stefan Reinauerb56f2d02010-03-25 22:17:36 +0000153 u16 reg16;
154
Yidi Lin5ef258b2022-08-10 14:59:18 +0800155 hci_t *controller = new_controller();
Patrick Rudolphac4819d2017-02-25 12:32:06 +0100156 controller->pcidev = addr;
Yidi Lin5ef258b2022-08-10 14:59:18 +0800157 controller->instance = xzalloc(sizeof(uhci_t));
Anton Kochkov1c36ead2012-06-28 08:30:15 +0400158 controller->type = UHCI;
Patrick Georgid21f68b2008-09-02 16:06:22 +0000159 controller->start = uhci_start;
160 controller->stop = uhci_stop;
161 controller->reset = uhci_reset;
Nico Huber6e711c62012-11-12 16:20:32 +0100162 controller->init = uhci_reinit;
Patrick Georgid21f68b2008-09-02 16:06:22 +0000163 controller->shutdown = uhci_shutdown;
Patrick Georgid21f68b2008-09-02 16:06:22 +0000164 controller->bulk = uhci_bulk;
165 controller->control = uhci_control;
Patrick Georgi482af6d2013-05-24 15:48:56 +0200166 controller->set_address = generic_set_address;
167 controller->finish_device_config = NULL;
168 controller->destroy_device = NULL;
Patrick Georgi4727c072008-10-16 19:20:51 +0000169 controller->create_intr_queue = uhci_create_intr_queue;
170 controller->destroy_intr_queue = uhci_destroy_intr_queue;
171 controller->poll_intr_queue = uhci_poll_intr_queue;
Yidi Lin5ef258b2022-08-10 14:59:18 +0800172 init_device_entry(controller, 0);
173 UHCI_INST(controller)->roothub = controller->devices[0];
Patrick Georgid21f68b2008-09-02 16:06:22 +0000174
Stefan Reinauer8992e532013-05-02 16:16:41 -0700175 /* ~1 clears the register type indicator that is set to 1
176 * for IO space */
Yidi Lin5ef258b2022-08-10 14:59:18 +0800177 controller->reg_base = pci_read_config32(addr, 0x20) & ~1;
Patrick Georgid21f68b2008-09-02 16:06:22 +0000178
179 /* kill legacy support handler */
Yidi Lin5ef258b2022-08-10 14:59:18 +0800180 uhci_stop(controller);
181 mdelay(1);
182 uhci_reg_write16(controller, USBSTS, 0x3f);
Stefan Reinauer8992e532013-05-02 16:16:41 -0700183 reg16 = pci_read_config16(addr, 0xc0);
Stefan Reinauerb56f2d02010-03-25 22:17:36 +0000184 reg16 &= 0xdf80;
Yidi Lin5ef258b2022-08-10 14:59:18 +0800185 pci_write_config16(addr, 0xc0, reg16);
Patrick Georgid21f68b2008-09-02 16:06:22 +0000186
Yidi Lin5ef258b2022-08-10 14:59:18 +0800187 UHCI_INST(controller)->framelistptr = memalign(0x1000, 1024 * sizeof(flistp_t)); /* 4kb aligned to 4kb */
188 if (!UHCI_INST (controller)->framelistptr)
Patrick Georgi2e768e72011-11-04 11:50:03 +0100189 fatal("Not enough memory for USB frame list pointer.\n");
Stefan Reinauer5fe6e232009-07-31 11:39:55 +0000190
Yidi Lin5ef258b2022-08-10 14:59:18 +0800191 memset(UHCI_INST(controller)->framelistptr, 0,
192 1024 * sizeof(flistp_t));
Patrick Georgid21f68b2008-09-02 16:06:22 +0000193
Patrick Georgi4727c072008-10-16 19:20:51 +0000194 /* According to the *BSD UHCI code, this one is needed on some
195 PIIX chips, because otherwise they misbehave. It must be
196 added to the last chain.
197
198 FIXME: this leaks, if the driver should ever be reinited
199 for some reason. Not a problem now.
200 */
201 td_t *antiberserk = memalign(16, sizeof(td_t));
Stefan Reinauer5fe6e232009-07-31 11:39:55 +0000202 if (!antiberserk)
Patrick Georgi2e768e72011-11-04 11:50:03 +0100203 fatal("Not enough memory for chipset workaround.\n");
Patrick Georgi4727c072008-10-16 19:20:51 +0000204 memset(antiberserk, 0, sizeof(td_t));
205
Yidi Lin5ef258b2022-08-10 14:59:18 +0800206 UHCI_INST(controller)->qh_prei = memalign(16, sizeof(qh_t));
207 UHCI_INST(controller)->qh_intr = memalign(16, sizeof(qh_t));
208 UHCI_INST(controller)->qh_data = memalign(16, sizeof(qh_t));
209 UHCI_INST(controller)->qh_last = memalign(16, sizeof(qh_t));
Patrick Georgid21f68b2008-09-02 16:06:22 +0000210
Yidi Lin5ef258b2022-08-10 14:59:18 +0800211 if (!UHCI_INST (controller)->qh_prei ||
212 !UHCI_INST (controller)->qh_intr ||
213 !UHCI_INST (controller)->qh_data ||
214 !UHCI_INST (controller)->qh_last)
Patrick Georgi2e768e72011-11-04 11:50:03 +0100215 fatal("Not enough memory for USB controller queues.\n");
Stefan Reinauer5fe6e232009-07-31 11:39:55 +0000216
Yidi Lin5ef258b2022-08-10 14:59:18 +0800217 UHCI_INST(controller)->qh_prei->headlinkptr =
218 virt_to_phys(UHCI_INST(controller)->qh_intr) | FLISTP_QH;
219 UHCI_INST(controller)->qh_prei->elementlinkptr = 0 | FLISTP_TERMINATE;
Patrick Georgi4727c072008-10-16 19:20:51 +0000220
Yidi Lin5ef258b2022-08-10 14:59:18 +0800221 UHCI_INST(controller)->qh_intr->headlinkptr =
222 virt_to_phys(UHCI_INST(controller)->qh_data) | FLISTP_QH;
223 UHCI_INST(controller)->qh_intr->elementlinkptr = 0 | FLISTP_TERMINATE;
Patrick Georgid21f68b2008-09-02 16:06:22 +0000224
Yidi Lin5ef258b2022-08-10 14:59:18 +0800225 UHCI_INST(controller)->qh_data->headlinkptr =
226 virt_to_phys(UHCI_INST(controller)->qh_last) | FLISTP_QH;
227 UHCI_INST(controller)->qh_data->elementlinkptr = 0 | FLISTP_TERMINATE;
Patrick Georgid21f68b2008-09-02 16:06:22 +0000228
Yidi Lin5ef258b2022-08-10 14:59:18 +0800229 UHCI_INST(controller)->qh_last->headlinkptr = virt_to_phys(UHCI_INST(controller)->qh_data) | FLISTP_TERMINATE;
230 UHCI_INST(controller)->qh_last->elementlinkptr = virt_to_phys(antiberserk) | FLISTP_TERMINATE;
Patrick Georgid21f68b2008-09-02 16:06:22 +0000231
232 for (i = 0; i < 1024; i++) {
Yidi Lin5ef258b2022-08-10 14:59:18 +0800233 UHCI_INST(controller)->framelistptr[i] =
234 virt_to_phys(UHCI_INST(controller)->qh_prei) | FLISTP_QH;
Patrick Georgid21f68b2008-09-02 16:06:22 +0000235 }
Patrick Georgi4727c072008-10-16 19:20:51 +0000236 controller->devices[0]->controller = controller;
237 controller->devices[0]->init = uhci_rh_init;
Yidi Lin5ef258b2022-08-10 14:59:18 +0800238 controller->devices[0]->init(controller->devices[0]);
239 uhci_reset(controller);
240 uhci_reinit(controller);
Patrick Georgid21f68b2008-09-02 16:06:22 +0000241 return controller;
242}
243
244static void
Yidi Lin5ef258b2022-08-10 14:59:18 +0800245uhci_shutdown(hci_t *controller)
Patrick Georgid21f68b2008-09-02 16:06:22 +0000246{
247 if (controller == 0)
248 return;
Yidi Lin5ef258b2022-08-10 14:59:18 +0800249 detach_controller(controller);
Patrick Georgif42fdab2011-11-18 14:44:16 +0100250 uhci_reg_write16(controller, USBCMD,
251 uhci_reg_read16(controller, USBCMD) & 0); // stop work
Yidi Lin5ef258b2022-08-10 14:59:18 +0800252 free(UHCI_INST(controller)->framelistptr);
253 free(UHCI_INST(controller)->qh_prei);
254 free(UHCI_INST(controller)->qh_intr);
255 free(UHCI_INST(controller)->qh_data);
256 free(UHCI_INST(controller)->qh_last);
257 free(UHCI_INST(controller));
258 free(controller);
Patrick Georgid21f68b2008-09-02 16:06:22 +0000259}
260
261static void
Yidi Lin5ef258b2022-08-10 14:59:18 +0800262uhci_start(hci_t *controller)
Patrick Georgid21f68b2008-09-02 16:06:22 +0000263{
Patrick Georgif42fdab2011-11-18 14:44:16 +0100264 uhci_reg_write16(controller, USBCMD,
265 uhci_reg_read16(controller, USBCMD) | 1); // start work on schedule
Patrick Georgid21f68b2008-09-02 16:06:22 +0000266}
267
268static void
Yidi Lin5ef258b2022-08-10 14:59:18 +0800269uhci_stop(hci_t *controller)
Patrick Georgid21f68b2008-09-02 16:06:22 +0000270{
Patrick Georgif42fdab2011-11-18 14:44:16 +0100271 uhci_reg_write16(controller, USBCMD,
272 uhci_reg_read16(controller, USBCMD) & ~1); // stop work on schedule
Patrick Georgid21f68b2008-09-02 16:06:22 +0000273}
274
Keith Shortbf2c6932019-06-21 16:39:59 -0600275#define UHCI_SLEEP_TIME_US 30
276#define UHCI_TIMEOUT (USB_MAX_PROCESSING_TIME_US / UHCI_SLEEP_TIME_US)
Patrick Georgid21f68b2008-09-02 16:06:22 +0000277#define GET_TD(x) ((void*)(((unsigned int)(x))&~0xf))
278
279static td_t *
Yidi Lin5ef258b2022-08-10 14:59:18 +0800280wait_for_completed_qh(hci_t *controller, qh_t *qh)
Patrick Georgid21f68b2008-09-02 16:06:22 +0000281{
Keith Shortbf2c6932019-06-21 16:39:59 -0600282 int timeout = UHCI_TIMEOUT;
Yidi Lin5ef258b2022-08-10 14:59:18 +0800283 void *current = GET_TD(qh->elementlinkptr);
Patrick Georgib0b4a522011-11-24 11:55:46 +0100284 while (((qh->elementlinkptr & FLISTP_TERMINATE) == 0) && (timeout-- > 0)) {
Yidi Lin5ef258b2022-08-10 14:59:18 +0800285 if (current != GET_TD(qh->elementlinkptr)) {
286 current = GET_TD(qh->elementlinkptr);
Keith Shortbf2c6932019-06-21 16:39:59 -0600287 timeout = UHCI_TIMEOUT;
Patrick Georgid21f68b2008-09-02 16:06:22 +0000288 }
Patrick Georgif42fdab2011-11-18 14:44:16 +0100289 uhci_reg_write16(controller, USBSTS,
290 uhci_reg_read16(controller, USBSTS) | 0); // clear resettable registers
Keith Shortbf2c6932019-06-21 16:39:59 -0600291 udelay(UHCI_SLEEP_TIME_US);
Patrick Georgid21f68b2008-09-02 16:06:22 +0000292 }
Yidi Lin5ef258b2022-08-10 14:59:18 +0800293 return (GET_TD(qh->elementlinkptr) ==
294 0) ? 0 : GET_TD(phys_to_virt(qh->elementlinkptr));
Patrick Georgid21f68b2008-09-02 16:06:22 +0000295}
296
Patrick Georgid21f68b2008-09-02 16:06:22 +0000297static int
Yidi Lin5ef258b2022-08-10 14:59:18 +0800298maxlen(int size)
Patrick Georgid21f68b2008-09-02 16:06:22 +0000299{
300 return (size - 1) & 0x7ff;
301}
302
303static int
Yidi Lin5ef258b2022-08-10 14:59:18 +0800304min(int a, int b)
Patrick Georgid21f68b2008-09-02 16:06:22 +0000305{
306 if (a < b)
307 return a;
308 else
309 return b;
310}
311
312static int
Nico Huber2a976f02023-05-31 12:49:42 +0000313uhci_control(usbdev_t *dev, direction_t dir, int drlen, void *devreq, const int dalen,
Patrick Georgid21f68b2008-09-02 16:06:22 +0000314 unsigned char *data)
315{
316 int endp = 0; /* this is control: always 0 */
317 int mlen = dev->endpoints[0].maxpacketsize;
318 int count = (2 + (dalen + mlen - 1) / mlen);
319 unsigned short req = ((unsigned short *) devreq)[0];
320 int i;
Yidi Lin5ef258b2022-08-10 14:59:18 +0800321 td_t *tds = memalign(16, sizeof(td_t) * count);
Francois Toguo651d8dd2019-03-27 10:30:20 -0700322 if (!tds)
323 fatal("Not enough memory for uhci control.\n");
Yidi Lin5ef258b2022-08-10 14:59:18 +0800324 memset(tds, 0, sizeof(td_t) * count);
Patrick Georgid21f68b2008-09-02 16:06:22 +0000325 count--; /* to compensate for 0-indexed array */
326 for (i = 0; i < count; i++) {
Yidi Lin5ef258b2022-08-10 14:59:18 +0800327 tds[i].ptr = virt_to_phys(&tds[i + 1]) | TD_DEPTH_FIRST;
Patrick Georgid21f68b2008-09-02 16:06:22 +0000328 }
Patrick Georgib0b4a522011-11-24 11:55:46 +0100329 tds[count].ptr = 0 | TD_DEPTH_FIRST | TD_TERMINATE;
Patrick Georgid21f68b2008-09-02 16:06:22 +0000330
Patrick Georgib0b4a522011-11-24 11:55:46 +0100331 tds[0].token = UHCI_SETUP |
332 dev->address << TD_DEVADDR_SHIFT |
333 endp << TD_EP_SHIFT |
334 TD_TOGGLE_DATA0 |
335 maxlen(drlen) << TD_MAXLEN_SHIFT;
Yidi Lin5ef258b2022-08-10 14:59:18 +0800336 tds[0].bufptr = virt_to_phys(devreq);
Patrick Georgib0b4a522011-11-24 11:55:46 +0100337 tds[0].ctrlsts = (3 << TD_COUNTER_SHIFT) |
338 (dev->speed?TD_LOWSPEED:0) |
339 TD_STATUS_ACTIVE;
Patrick Georgid21f68b2008-09-02 16:06:22 +0000340
341 int toggle = 1;
Nico Huber2a976f02023-05-31 12:49:42 +0000342 int len_left = dalen;
Patrick Georgid21f68b2008-09-02 16:06:22 +0000343 for (i = 1; i < count; i++) {
Patrick Georgid78691d2010-06-07 13:58:17 +0000344 switch (dir) {
Patrick Georgib0b4a522011-11-24 11:55:46 +0100345 case SETUP: tds[i].token = UHCI_SETUP; break;
346 case IN: tds[i].token = UHCI_IN; break;
347 case OUT: tds[i].token = UHCI_OUT; break;
Patrick Georgid78691d2010-06-07 13:58:17 +0000348 }
Patrick Georgib0b4a522011-11-24 11:55:46 +0100349 tds[i].token |= dev->address << TD_DEVADDR_SHIFT |
350 endp << TD_EP_SHIFT |
Nico Huber2a976f02023-05-31 12:49:42 +0000351 maxlen(min(mlen, len_left)) << TD_MAXLEN_SHIFT |
Patrick Georgib0b4a522011-11-24 11:55:46 +0100352 toggle << TD_TOGGLE_SHIFT;
Yidi Lin5ef258b2022-08-10 14:59:18 +0800353 tds[i].bufptr = virt_to_phys(data);
Patrick Georgib0b4a522011-11-24 11:55:46 +0100354 tds[i].ctrlsts = (3 << TD_COUNTER_SHIFT) |
355 (dev->speed?TD_LOWSPEED:0) |
356 TD_STATUS_ACTIVE;
Patrick Georgid21f68b2008-09-02 16:06:22 +0000357 toggle ^= 1;
Nico Huber2a976f02023-05-31 12:49:42 +0000358 len_left -= mlen;
Patrick Georgid21f68b2008-09-02 16:06:22 +0000359 data += mlen;
360 }
361
Nico Hubercefec0e2012-05-16 15:04:27 +0200362 tds[count].token = ((dir == OUT) ? UHCI_IN : UHCI_OUT) |
Patrick Georgib0b4a522011-11-24 11:55:46 +0100363 dev->address << TD_DEVADDR_SHIFT |
364 endp << TD_EP_SHIFT |
365 maxlen(0) << TD_MAXLEN_SHIFT |
366 TD_TOGGLE_DATA1;
Stefan Reinauerb56f2d02010-03-25 22:17:36 +0000367 tds[count].bufptr = 0;
Nico Hubercefec0e2012-05-16 15:04:27 +0200368 tds[count].ctrlsts = (0 << TD_COUNTER_SHIFT) | /* as Linux 2.4.10 does */
Patrick Georgib0b4a522011-11-24 11:55:46 +0100369 (dev->speed?TD_LOWSPEED:0) |
370 TD_STATUS_ACTIVE;
Yidi Lin5ef258b2022-08-10 14:59:18 +0800371 UHCI_INST(dev->controller)->qh_data->elementlinkptr =
372 virt_to_phys(tds) & ~(FLISTP_QH | FLISTP_TERMINATE);
373 td_t *td = wait_for_completed_qh(dev->controller,
374 UHCI_INST(dev->controller)->
Patrick Georgid21f68b2008-09-02 16:06:22 +0000375 qh_data);
376 int result;
377 if (td == 0) {
Nico Huber2a976f02023-05-31 12:49:42 +0000378 result = dalen; /* TODO: We should return the actually transferred length. */
Patrick Georgid21f68b2008-09-02 16:06:22 +0000379 } else {
Yidi Lin5ef258b2022-08-10 14:59:18 +0800380 usb_debug("control packet, req %x\n", req);
381 td_dump(td);
Julius Wernere9738db2013-02-21 13:41:40 -0800382 result = -1;
Patrick Georgid21f68b2008-09-02 16:06:22 +0000383 }
Yidi Lin5ef258b2022-08-10 14:59:18 +0800384 free(tds);
Patrick Georgid21f68b2008-09-02 16:06:22 +0000385 return result;
386}
387
Patrick Georgid21f68b2008-09-02 16:06:22 +0000388static td_t *
Yidi Lin5ef258b2022-08-10 14:59:18 +0800389create_schedule(int numpackets)
Patrick Georgid21f68b2008-09-02 16:06:22 +0000390{
391 if (numpackets == 0)
392 return 0;
Yidi Lin5ef258b2022-08-10 14:59:18 +0800393 td_t *tds = memalign(16, sizeof(td_t) * numpackets);
Francois Toguo651d8dd2019-03-27 10:30:20 -0700394 if (!tds)
395 fatal("Not enough memory for packets scheduling.\n");
Yidi Lin5ef258b2022-08-10 14:59:18 +0800396 memset(tds, 0, sizeof(td_t) * numpackets);
Patrick Georgid21f68b2008-09-02 16:06:22 +0000397 int i;
398 for (i = 0; i < numpackets; i++) {
Yidi Lin5ef258b2022-08-10 14:59:18 +0800399 tds[i].ptr = virt_to_phys(&tds[i + 1]) | TD_DEPTH_FIRST;
Patrick Georgid21f68b2008-09-02 16:06:22 +0000400 }
Patrick Georgib0b4a522011-11-24 11:55:46 +0100401 tds[numpackets - 1].ptr = 0 | TD_TERMINATE;
Patrick Georgid21f68b2008-09-02 16:06:22 +0000402 return tds;
403}
404
405static void
Yidi Lin5ef258b2022-08-10 14:59:18 +0800406fill_schedule(td_t *td, endpoint_t *ep, int length, unsigned char *data,
Patrick Georgid21f68b2008-09-02 16:06:22 +0000407 int *toggle)
408{
Patrick Georgicfaa0812010-06-11 14:25:40 +0000409 switch (ep->direction) {
Patrick Georgib0b4a522011-11-24 11:55:46 +0100410 case IN: td->token = UHCI_IN; break;
411 case OUT: td->token = UHCI_OUT; break;
412 case SETUP: td->token = UHCI_SETUP; break;
Patrick Georgicfaa0812010-06-11 14:25:40 +0000413 }
Patrick Georgib0b4a522011-11-24 11:55:46 +0100414 td->token |= ep->dev->address << TD_DEVADDR_SHIFT |
415 (ep->endpoint & 0xf) << TD_EP_SHIFT |
Yidi Lin5ef258b2022-08-10 14:59:18 +0800416 maxlen(length) << TD_MAXLEN_SHIFT |
Patrick Georgib0b4a522011-11-24 11:55:46 +0100417 (*toggle & 1) << TD_TOGGLE_SHIFT;
Yidi Lin5ef258b2022-08-10 14:59:18 +0800418 td->bufptr = virt_to_phys(data);
Patrick Georgib0b4a522011-11-24 11:55:46 +0100419 td->ctrlsts = ((ep->direction == SETUP?3:0) << TD_COUNTER_SHIFT) |
Nico Hubercefec0e2012-05-16 15:04:27 +0200420 (ep->dev->speed?TD_LOWSPEED:0) |
Patrick Georgib0b4a522011-11-24 11:55:46 +0100421 TD_STATUS_ACTIVE;
Patrick Georgid21f68b2008-09-02 16:06:22 +0000422 *toggle ^= 1;
423}
424
425static int
Yidi Lin5ef258b2022-08-10 14:59:18 +0800426run_schedule(usbdev_t *dev, td_t *td)
Patrick Georgid21f68b2008-09-02 16:06:22 +0000427{
Yidi Lin5ef258b2022-08-10 14:59:18 +0800428 UHCI_INST(dev->controller)->qh_data->elementlinkptr =
429 virt_to_phys(td) & ~(FLISTP_QH | FLISTP_TERMINATE);
430 td = wait_for_completed_qh(dev->controller,
431 UHCI_INST(dev->controller)->qh_data);
Patrick Georgid21f68b2008-09-02 16:06:22 +0000432 if (td == 0) {
433 return 0;
434 } else {
Yidi Lin5ef258b2022-08-10 14:59:18 +0800435 td_dump(td);
Patrick Georgid21f68b2008-09-02 16:06:22 +0000436 return 1;
437 }
438}
439
440/* finalize == 1: if data is of packet aligned size, add a zero length packet */
441static int
Nico Huber2a976f02023-05-31 12:49:42 +0000442uhci_bulk(endpoint_t *ep, const int dalen, u8 *data, int finalize)
Patrick Georgid21f68b2008-09-02 16:06:22 +0000443{
444 int maxpsize = ep->maxpacketsize;
445 if (maxpsize == 0)
Patrick Georgi2e768e72011-11-04 11:50:03 +0100446 fatal("MaxPacketSize == 0!!!");
Nico Huber2a976f02023-05-31 12:49:42 +0000447 int numpackets = (dalen + maxpsize - 1) / maxpsize;
448 if (finalize && ((dalen % maxpsize) == 0)) {
Patrick Georgid2cb7ea2012-10-03 08:23:56 +0200449 numpackets++;
450 }
Patrick Georgid21f68b2008-09-02 16:06:22 +0000451 if (numpackets == 0)
452 return 0;
Yidi Lin5ef258b2022-08-10 14:59:18 +0800453 td_t *tds = create_schedule(numpackets);
Patrick Georgid21f68b2008-09-02 16:06:22 +0000454 int i = 0, toggle = ep->toggle;
Nico Huber2a976f02023-05-31 12:49:42 +0000455 int len_left = dalen;
456 while ((len_left > 0) || ((len_left == 0) && (finalize != 0))) {
457 fill_schedule(&tds[i], ep, min(len_left, maxpsize), data,
Patrick Georgid21f68b2008-09-02 16:06:22 +0000458 &toggle);
459 i++;
460 data += maxpsize;
Nico Huber2a976f02023-05-31 12:49:42 +0000461 len_left -= maxpsize;
Patrick Georgid21f68b2008-09-02 16:06:22 +0000462 }
Yidi Lin5ef258b2022-08-10 14:59:18 +0800463 if (run_schedule(ep->dev, tds) == 1) {
464 free(tds);
Julius Wernere9738db2013-02-21 13:41:40 -0800465 return -1;
Patrick Georgid21f68b2008-09-02 16:06:22 +0000466 }
467 ep->toggle = toggle;
Yidi Lin5ef258b2022-08-10 14:59:18 +0800468 free(tds);
Nico Huber2a976f02023-05-31 12:49:42 +0000469 return dalen; /* TODO: We should return the actually transferred length. */
Patrick Georgid21f68b2008-09-02 16:06:22 +0000470}
471
Patrick Georgi4727c072008-10-16 19:20:51 +0000472typedef struct {
473 qh_t *qh;
474 td_t *tds;
475 td_t *last_td;
476 u8 *data;
477 int lastread;
478 int total;
479 int reqsize;
480} intr_q;
481
482/* create and hook-up an intr queue into device schedule */
483static void*
Yidi Lin5ef258b2022-08-10 14:59:18 +0800484uhci_create_intr_queue(endpoint_t *ep, int reqsize, int reqcount, int reqtiming)
Patrick Georgi4727c072008-10-16 19:20:51 +0000485{
486 u8 *data = malloc(reqsize*reqcount);
487 td_t *tds = memalign(16, sizeof(td_t) * reqcount);
488 qh_t *qh = memalign(16, sizeof(qh_t));
489
Stefan Reinauer5fe6e232009-07-31 11:39:55 +0000490 if (!data || !tds || !qh)
Patrick Georgi2e768e72011-11-04 11:50:03 +0100491 fatal("Not enough memory to create USB intr queue prerequisites.\n");
Stefan Reinauer5fe6e232009-07-31 11:39:55 +0000492
Patrick Georgib0b4a522011-11-24 11:55:46 +0100493 qh->elementlinkptr = virt_to_phys(tds);
Patrick Georgi4727c072008-10-16 19:20:51 +0000494
495 intr_q *q = malloc(sizeof(intr_q));
Stefan Reinauer5fe6e232009-07-31 11:39:55 +0000496 if (!q)
Patrick Georgi2e768e72011-11-04 11:50:03 +0100497 fatal("Not enough memory to create USB intr queue.\n");
Patrick Georgi4727c072008-10-16 19:20:51 +0000498 q->qh = qh;
499 q->tds = tds;
500 q->data = data;
501 q->lastread = 0;
502 q->total = reqcount;
503 q->reqsize = reqsize;
504 q->last_td = &tds[reqcount - 1];
505
Yidi Lin5ef258b2022-08-10 14:59:18 +0800506 memset(tds, 0, sizeof(td_t) * reqcount);
Patrick Georgi4727c072008-10-16 19:20:51 +0000507 int i;
508 for (i = 0; i < reqcount; i++) {
Yidi Lin5ef258b2022-08-10 14:59:18 +0800509 tds[i].ptr = virt_to_phys(&tds[i + 1]);
Patrick Georgi4727c072008-10-16 19:20:51 +0000510
Patrick Georgicfaa0812010-06-11 14:25:40 +0000511 switch (ep->direction) {
Patrick Georgib0b4a522011-11-24 11:55:46 +0100512 case IN: tds[i].token = UHCI_IN; break;
513 case OUT: tds[i].token = UHCI_OUT; break;
514 case SETUP: tds[i].token = UHCI_SETUP; break;
Patrick Georgicfaa0812010-06-11 14:25:40 +0000515 }
Patrick Georgib0b4a522011-11-24 11:55:46 +0100516 tds[i].token |= ep->dev->address << TD_DEVADDR_SHIFT |
517 (ep->endpoint & 0xf) << TD_EP_SHIFT |
Yidi Lin5ef258b2022-08-10 14:59:18 +0800518 maxlen(reqsize) << TD_MAXLEN_SHIFT |
Patrick Georgib0b4a522011-11-24 11:55:46 +0100519 (ep->toggle & 1) << TD_TOGGLE_SHIFT;
Yidi Lin5ef258b2022-08-10 14:59:18 +0800520 tds[i].bufptr = virt_to_phys(data);
Patrick Georgib0b4a522011-11-24 11:55:46 +0100521 tds[i].ctrlsts = (0 << TD_COUNTER_SHIFT) |
Nico Hubercefec0e2012-05-16 15:04:27 +0200522 (ep->dev->speed?TD_LOWSPEED:0) |
Patrick Georgib0b4a522011-11-24 11:55:46 +0100523 TD_STATUS_ACTIVE;
Patrick Georgi4727c072008-10-16 19:20:51 +0000524 ep->toggle ^= 1;
525 data += reqsize;
526 }
Patrick Georgib0b4a522011-11-24 11:55:46 +0100527 tds[reqcount - 1].ptr = 0 | TD_TERMINATE;
Nico Huberce407e42012-11-20 17:27:46 +0100528
529 /* insert QH into framelist */
530 uhci_t *const uhcic = UHCI_INST(ep->dev->controller);
531 const u32 def_ptr = virt_to_phys(uhcic->qh_prei) | FLISTP_QH;
532 int nothing_placed = 1;
533 qh->headlinkptr = def_ptr;
534 for (i = 0; i < 1024; i += reqtiming) {
535 /* advance to the next free position */
536 while ((i < 1024) && (uhcic->framelistptr[i] != def_ptr)) ++i;
537 if (i < 1024) {
538 uhcic->framelistptr[i] = virt_to_phys(qh) | FLISTP_QH;
539 nothing_placed = 0;
540 }
Patrick Georgi4727c072008-10-16 19:20:51 +0000541 }
Nico Huberce407e42012-11-20 17:27:46 +0100542 if (nothing_placed) {
Dave Frodin6bf11cf2012-12-11 13:08:07 -0700543 usb_debug("Error: Failed to place UHCI interrupt queue "
Nico Huberce407e42012-11-20 17:27:46 +0100544 "head into framelist: no space left\n");
545 uhci_destroy_intr_queue(ep, q);
546 return NULL;
547 }
548
Patrick Georgi4727c072008-10-16 19:20:51 +0000549 return q;
550}
551
552/* remove queue from device schedule, dropping all data that came in */
553static void
Yidi Lin5ef258b2022-08-10 14:59:18 +0800554uhci_destroy_intr_queue(endpoint_t *ep, void *q_)
Patrick Georgi4727c072008-10-16 19:20:51 +0000555{
Nico Huberce407e42012-11-20 17:27:46 +0100556 intr_q *const q = (intr_q*)q_;
557
558 /* remove QH from framelist */
559 uhci_t *const uhcic = UHCI_INST(ep->dev->controller);
560 const u32 qh_ptr = virt_to_phys(q->qh) | FLISTP_QH;
561 const u32 def_ptr = virt_to_phys(uhcic->qh_prei) | FLISTP_QH;
Patrick Georgi4727c072008-10-16 19:20:51 +0000562 int i;
Nico Huberce407e42012-11-20 17:27:46 +0100563 for (i = 0; i < 1024; ++i) {
564 if (uhcic->framelistptr[i] == qh_ptr)
565 uhcic->framelistptr[i] = def_ptr;
Patrick Georgi4727c072008-10-16 19:20:51 +0000566 }
Nico Huberce407e42012-11-20 17:27:46 +0100567
Patrick Georgi4727c072008-10-16 19:20:51 +0000568 free(q->data);
569 free(q->tds);
570 free(q->qh);
571 free(q);
572}
573
574/* read one intr-packet from queue, if available. extend the queue for new input.
575 return NULL if nothing new available.
576 Recommended use: while (data=poll_intr_queue(q)) process(data);
577 */
578static u8*
Yidi Lin5ef258b2022-08-10 14:59:18 +0800579uhci_poll_intr_queue(void *q_)
Patrick Georgi4727c072008-10-16 19:20:51 +0000580{
581 intr_q *q = (intr_q*)q_;
Patrick Georgib0b4a522011-11-24 11:55:46 +0100582 if ((q->tds[q->lastread].ctrlsts & TD_STATUS_ACTIVE) == 0) {
Patrick Georgi4727c072008-10-16 19:20:51 +0000583 int current = q->lastread;
584 int previous;
585 if (q->lastread == 0) {
586 previous = q->total - 1;
587 } else {
588 previous = q->lastread - 1;
589 }
Patrick Georgib0b4a522011-11-24 11:55:46 +0100590 q->tds[previous].ctrlsts &= ~TD_STATUS_MASK;
591 q->tds[previous].ptr = 0 | TD_TERMINATE;
Patrick Georgi4727c072008-10-16 19:20:51 +0000592 if (q->last_td != &q->tds[previous]) {
Patrick Georgib0b4a522011-11-24 11:55:46 +0100593 q->last_td->ptr = virt_to_phys(&q->tds[previous]) & ~TD_TERMINATE;
Patrick Georgi4727c072008-10-16 19:20:51 +0000594 q->last_td = &q->tds[previous];
595 }
Patrick Georgib0b4a522011-11-24 11:55:46 +0100596 q->tds[previous].ctrlsts |= TD_STATUS_ACTIVE;
Patrick Georgi4727c072008-10-16 19:20:51 +0000597 q->lastread = (q->lastread + 1) % q->total;
Nico Huberbe58fee2012-11-22 11:12:13 +0100598 if (!(q->tds[current].ctrlsts & TD_STATUS_MASK))
599 return &q->data[current*q->reqsize];
Patrick Georgi4727c072008-10-16 19:20:51 +0000600 }
Nico Huber8c4d2f32012-11-20 17:49:00 +0100601 /* reset queue if we fully processed it after underrun */
602 else if (q->qh->elementlinkptr & FLISTP_TERMINATE) {
603 usb_debug("resetting underrun uhci interrupt queue.\n");
604 q->qh->elementlinkptr = virt_to_phys(q->tds + q->lastread);
605 }
Patrick Georgi4727c072008-10-16 19:20:51 +0000606 return NULL;
607}
608
Patrick Georgid21f68b2008-09-02 16:06:22 +0000609void
Yidi Lin5ef258b2022-08-10 14:59:18 +0800610uhci_reg_write32(hci_t *ctrl, usbreg reg, u32 value)
Patrick Georgid21f68b2008-09-02 16:06:22 +0000611{
Yidi Lin5ef258b2022-08-10 14:59:18 +0800612 outl(value, ctrl->reg_base + reg);
Patrick Georgid21f68b2008-09-02 16:06:22 +0000613}
614
615u32
Yidi Lin5ef258b2022-08-10 14:59:18 +0800616uhci_reg_read32(hci_t *ctrl, usbreg reg)
Patrick Georgid21f68b2008-09-02 16:06:22 +0000617{
Yidi Lin5ef258b2022-08-10 14:59:18 +0800618 return inl(ctrl->reg_base + reg);
Patrick Georgid21f68b2008-09-02 16:06:22 +0000619}
620
621void
Yidi Lin5ef258b2022-08-10 14:59:18 +0800622uhci_reg_write16(hci_t *ctrl, usbreg reg, u16 value)
Patrick Georgid21f68b2008-09-02 16:06:22 +0000623{
Yidi Lin5ef258b2022-08-10 14:59:18 +0800624 outw(value, ctrl->reg_base + reg);
Patrick Georgid21f68b2008-09-02 16:06:22 +0000625}
626
627u16
Yidi Lin5ef258b2022-08-10 14:59:18 +0800628uhci_reg_read16(hci_t *ctrl, usbreg reg)
Patrick Georgid21f68b2008-09-02 16:06:22 +0000629{
Yidi Lin5ef258b2022-08-10 14:59:18 +0800630 return inw(ctrl->reg_base + reg);
Patrick Georgid21f68b2008-09-02 16:06:22 +0000631}
632
633void
Yidi Lin5ef258b2022-08-10 14:59:18 +0800634uhci_reg_write8(hci_t *ctrl, usbreg reg, u8 value)
Patrick Georgid21f68b2008-09-02 16:06:22 +0000635{
Yidi Lin5ef258b2022-08-10 14:59:18 +0800636 outb(value, ctrl->reg_base + reg);
Patrick Georgid21f68b2008-09-02 16:06:22 +0000637}
638
639u8
Yidi Lin5ef258b2022-08-10 14:59:18 +0800640uhci_reg_read8(hci_t *ctrl, usbreg reg)
Patrick Georgid21f68b2008-09-02 16:06:22 +0000641{
Yidi Lin5ef258b2022-08-10 14:59:18 +0800642 return inb(ctrl->reg_base + reg);
Patrick Georgid21f68b2008-09-02 16:06:22 +0000643}