source: mainline/uspace/drv/bus/usb/ehci/pci.c@ 70d72dd

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 70d72dd was 5203e256, checked in by Martin Decky <martin@…>, 14 years ago

keep the drivers source tree tidy by using logical subdirectories

  • Property mode set to 100644
File size: 11.2 KB
RevLine 
[40a5d40]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 */
[79ae36dd]28
[40a5d40]29/**
[0969e45e]30 * @addtogroup drvusbehci
[40a5d40]31 * @{
32 */
33/**
34 * @file
[0969e45e]35 * PCI related functions needed by the EHCI driver.
[40a5d40]36 */
[79ae36dd]37
[40a5d40]38#include <errno.h>
[109d55c]39#include <str_error.h>
[40a5d40]40#include <assert.h>
41#include <as.h>
42#include <devman.h>
43#include <ddi.h>
44#include <libarch/ddi.h>
45#include <device/hw_res.h>
46
47#include <usb/debug.h>
48#include <pci_dev_iface.h>
49
50#include "pci.h"
51
52#define PAGE_SIZE_MASK 0xfffff000
[13927cf]53
[40a5d40]54#define HCC_PARAMS_OFFSET 0x8
55#define HCC_PARAMS_EECP_MASK 0xff
56#define HCC_PARAMS_EECP_OFFSET 8
57
[0d3167e]58#define CMD_OFFSET 0x0
[a948c23]59#define STS_OFFSET 0x4
[17d1542]60#define INT_OFFSET 0x8
[a948c23]61#define CFG_OFFSET 0x40
[0d3167e]62
[40a5d40]63#define USBCMD_RUN 1
[17d1542]64#define USBSTS_HALTED (1 << 12)
[40a5d40]65
66#define USBLEGSUP_OFFSET 0
67#define USBLEGSUP_BIOS_CONTROL (1 << 16)
68#define USBLEGSUP_OS_CONTROL (1 << 24)
69#define USBLEGCTLSTS_OFFSET 4
70
[17d1542]71#define DEFAULT_WAIT 1000
[40a5d40]72#define WAIT_STEP 10
73
[17d1542]74#define PCI_READ(size) \
75do { \
[79ae36dd]76 async_sess_t *parent_sess = \
77 devman_parent_device_connect(EXCHANGE_SERIALIZE, dev->handle, \
78 IPC_FLAG_BLOCKING); \
79 if (!parent_sess) \
80 return ENOMEM; \
81 \
82 sysarg_t add = (sysarg_t) address; \
[17d1542]83 sysarg_t val; \
[79ae36dd]84 \
85 async_exch_t *exch = async_exchange_begin(parent_sess); \
86 \
[17d1542]87 const int ret = \
[79ae36dd]88 async_req_2_1(exch, DEV_IFACE_ID(PCI_DEV_IFACE), \
[17d1542]89 IPC_M_CONFIG_SPACE_READ_##size, add, &val); \
[79ae36dd]90 \
91 async_exchange_end(exch); \
92 async_hangup(parent_sess); \
93 \
[17d1542]94 assert(value); \
[79ae36dd]95 \
[17d1542]96 *value = val; \
97 return ret; \
[79ae36dd]98} while (0)
[17d1542]99
[c060090]100static int pci_read32(const ddf_dev_t *dev, int address, uint32_t *value)
[17d1542]101{
102 PCI_READ(32);
103}
[79ae36dd]104
[c060090]105static int pci_read16(const ddf_dev_t *dev, int address, uint16_t *value)
[17d1542]106{
107 PCI_READ(16);
108}
[79ae36dd]109
[c060090]110static int pci_read8(const ddf_dev_t *dev, int address, uint8_t *value)
[17d1542]111{
112 PCI_READ(8);
113}
[79ae36dd]114
[17d1542]115#define PCI_WRITE(size) \
116do { \
[79ae36dd]117 async_sess_t *parent_sess = \
118 devman_parent_device_connect(EXCHANGE_SERIALIZE, dev->handle, \
119 IPC_FLAG_BLOCKING); \
120 if (!parent_sess) \
121 return ENOMEM; \
122 \
123 sysarg_t add = (sysarg_t) address; \
[17d1542]124 sysarg_t val = value; \
[79ae36dd]125 \
126 async_exch_t *exch = async_exchange_begin(parent_sess); \
127 \
[17d1542]128 const int ret = \
[79ae36dd]129 async_req_3_0(exch, DEV_IFACE_ID(PCI_DEV_IFACE), \
[17d1542]130 IPC_M_CONFIG_SPACE_WRITE_##size, add, val); \
[79ae36dd]131 \
132 async_exchange_end(exch); \
133 async_hangup(parent_sess); \
134 \
[17d1542]135 return ret; \
136} while(0)
137
[c060090]138static int pci_write32(const ddf_dev_t *dev, int address, uint32_t value)
[17d1542]139{
140 PCI_WRITE(32);
141}
[79ae36dd]142
[c060090]143static int pci_write16(const ddf_dev_t *dev, int address, uint16_t value)
[17d1542]144{
145 PCI_WRITE(16);
146}
[79ae36dd]147
[c060090]148static int pci_write8(const ddf_dev_t *dev, int address, uint8_t value)
[17d1542]149{
150 PCI_WRITE(8);
151}
152
[40a5d40]153/** Get address of registers and IRQ for given device.
154 *
155 * @param[in] dev Device asking for the addresses.
[13927cf]156 * @param[out] mem_reg_address Base address of the memory range.
157 * @param[out] mem_reg_size Size of the memory range.
[40a5d40]158 * @param[out] irq_no IRQ assigned to the device.
159 * @return Error code.
160 */
[c060090]161int pci_get_my_registers(const ddf_dev_t *dev,
[40a5d40]162 uintptr_t *mem_reg_address, size_t *mem_reg_size, int *irq_no)
163{
164 assert(dev != NULL);
[79ae36dd]165
166 async_sess_t *parent_sess =
167 devman_parent_device_connect(EXCHANGE_SERIALIZE, dev->handle,
168 IPC_FLAG_BLOCKING);
169 if (!parent_sess)
170 return ENOMEM;
171
[40a5d40]172 hw_resource_list_t hw_resources;
[79ae36dd]173 int rc = hw_res_get_resource_list(parent_sess, &hw_resources);
[40a5d40]174 if (rc != EOK) {
[79ae36dd]175 async_hangup(parent_sess);
[4ed80ce8]176 return rc;
[40a5d40]177 }
[79ae36dd]178
[40a5d40]179 uintptr_t mem_address = 0;
180 size_t mem_size = 0;
181 bool mem_found = false;
[79ae36dd]182
[40a5d40]183 int irq = 0;
184 bool irq_found = false;
[79ae36dd]185
[40a5d40]186 size_t i;
187 for (i = 0; i < hw_resources.count; i++) {
188 hw_resource_t *res = &hw_resources.resources[i];
[79ae36dd]189 switch (res->type) {
[4ed80ce8]190 case INTERRUPT:
191 irq = res->res.interrupt.irq;
192 irq_found = true;
193 usb_log_debug2("Found interrupt: %d.\n", irq);
194 break;
195 case MEM_RANGE:
196 if (res->res.mem_range.address != 0
197 && res->res.mem_range.size != 0 ) {
198 mem_address = res->res.mem_range.address;
199 mem_size = res->res.mem_range.size;
[4125b7d]200 usb_log_debug2("Found mem: %" PRIxn" %zu.\n",
[4ed80ce8]201 mem_address, mem_size);
202 mem_found = true;
[79ae36dd]203 }
[4ed80ce8]204 default:
205 break;
[40a5d40]206 }
207 }
[79ae36dd]208
[4ed80ce8]209 if (mem_found && irq_found) {
210 *mem_reg_address = mem_address;
211 *mem_reg_size = mem_size;
212 *irq_no = irq;
213 rc = EOK;
214 } else {
[40a5d40]215 rc = ENOENT;
216 }
[79ae36dd]217
218 async_hangup(parent_sess);
[40a5d40]219 return rc;
220}
221/*----------------------------------------------------------------------------*/
[13927cf]222/** Calls the PCI driver with a request to enable interrupts
223 *
224 * @param[in] device Device asking for interrupts
225 * @return Error code.
226 */
[c060090]227int pci_enable_interrupts(const ddf_dev_t *device)
[40a5d40]228{
[79ae36dd]229 async_sess_t *parent_sess =
230 devman_parent_device_connect(EXCHANGE_SERIALIZE, device->handle,
231 IPC_FLAG_BLOCKING);
232 if (!parent_sess)
233 return ENOMEM;
234
235 const bool enabled = hw_res_enable_interrupt(parent_sess);
236 async_hangup(parent_sess);
237
[40a5d40]238 return enabled ? EOK : EIO;
239}
240/*----------------------------------------------------------------------------*/
[13927cf]241/** Implements BIOS handoff routine as decribed in EHCI spec
242 *
243 * @param[in] device Device asking for interrupts
244 * @return Error code.
245 */
[c060090]246int pci_disable_legacy(
247 const ddf_dev_t *device, uintptr_t reg_base, size_t reg_size, int irq)
[40a5d40]248{
249 assert(device);
[17d1542]250 (void) pci_read16;
251 (void) pci_read8;
252 (void) pci_write16;
[40a5d40]253
[17d1542]254#define CHECK_RET_RETURN(ret, message...) \
[4ed80ce8]255 if (ret != EOK) { \
256 usb_log_error(message); \
257 return ret; \
258 } else (void)0
259
[c060090]260 /* Map EHCI registers */
[17d1542]261 void *regs = NULL;
[c060090]262 int ret = pio_enable((void*)reg_base, reg_size, &regs);
[109d55c]263 CHECK_RET_RETURN(ret, "Failed to map registers %p: %s.\n",
264 (void *) reg_base, str_error(ret));
[40a5d40]265
[4ed80ce8]266 const uint32_t hcc_params =
[17d1542]267 *(uint32_t*)(regs + HCC_PARAMS_OFFSET);
[40a5d40]268 usb_log_debug("Value of hcc params register: %x.\n", hcc_params);
[13927cf]269
270 /* Read value of EHCI Extended Capabilities Pointer
[17d1542]271 * position of EEC registers (points to PCI config space) */
272 const uint32_t eecp =
[4ed80ce8]273 (hcc_params >> HCC_PARAMS_EECP_OFFSET) & HCC_PARAMS_EECP_MASK;
[40a5d40]274 usb_log_debug("Value of EECP: %x.\n", eecp);
275
[13927cf]276 /* Read the first EEC. i.e. Legacy Support register */
[17d1542]277 uint32_t usblegsup;
278 ret = pci_read32(device, eecp + USBLEGSUP_OFFSET, &usblegsup);
[109d55c]279 CHECK_RET_RETURN(ret, "Failed to read USBLEGSUP: %s.\n", str_error(ret));
[50340bf]280 usb_log_debug("USBLEGSUP: %" PRIx32 ".\n", usblegsup);
[40a5d40]281
[13927cf]282 /* Request control from firmware/BIOS, by writing 1 to highest byte.
283 * (OS Control semaphore)*/
[17d1542]284 usb_log_debug("Requesting OS control.\n");
285 ret = pci_write8(device, eecp + USBLEGSUP_OFFSET + 3, 1);
[109d55c]286 CHECK_RET_RETURN(ret, "Failed to request OS EHCI control: %s.\n",
287 str_error(ret));
[40a5d40]288
[4ed80ce8]289 size_t wait = 0;
[13927cf]290 /* Wait for BIOS to release control. */
[17d1542]291 ret = pci_read32(device, eecp + USBLEGSUP_OFFSET, &usblegsup);
292 while ((wait < DEFAULT_WAIT) && (usblegsup & USBLEGSUP_BIOS_CONTROL)) {
[40a5d40]293 async_usleep(WAIT_STEP);
[17d1542]294 ret = pci_read32(device, eecp + USBLEGSUP_OFFSET, &usblegsup);
[40a5d40]295 wait += WAIT_STEP;
296 }
297
[13927cf]298
[17d1542]299 if ((usblegsup & USBLEGSUP_BIOS_CONTROL) == 0) {
[4125b7d]300 usb_log_info("BIOS released control after %zu usec.\n", wait);
[4ed80ce8]301 } else {
[13927cf]302 /* BIOS failed to hand over control, this should not happen. */
[67352d2]303 usb_log_warning( "BIOS failed to release control after "
[4125b7d]304 "%zu usecs, force it.\n", wait);
[17d1542]305 ret = pci_write32(device, eecp + USBLEGSUP_OFFSET,
[40a5d40]306 USBLEGSUP_OS_CONTROL);
[109d55c]307 CHECK_RET_RETURN(ret, "Failed to force OS control: %s.\n",
308 str_error(ret));
[17d1542]309 /* Check capability type here, A value of 01h
310 * identifies the capability as Legacy Support.
311 * This extended capability requires one
312 * additional 32-bit register for control/status information,
313 * and this register is located at offset EECP+04h
314 * */
315 if ((usblegsup & 0xff) == 1) {
316 /* Read the second EEC
317 * Legacy Support and Control register */
318 uint32_t usblegctlsts;
319 ret = pci_read32(
320 device, eecp + USBLEGCTLSTS_OFFSET, &usblegctlsts);
321 CHECK_RET_RETURN(ret,
[109d55c]322 "Failed to get USBLEGCTLSTS: %s.\n", str_error(ret));
[50340bf]323 usb_log_debug("USBLEGCTLSTS: %" PRIx32 ".\n",
[17d1542]324 usblegctlsts);
325 /* Zero SMI enables in legacy control register.
326 * It should prevent pre-OS code from interfering. */
327 ret = pci_write32(device, eecp + USBLEGCTLSTS_OFFSET,
328 0xe0000000); /* three upper bits are WC */
329 CHECK_RET_RETURN(ret,
330 "Failed(%d) zero USBLEGCTLSTS.\n", ret);
[c060090]331 udelay(10);
[17d1542]332 ret = pci_read32(
333 device, eecp + USBLEGCTLSTS_OFFSET, &usblegctlsts);
334 CHECK_RET_RETURN(ret,
[109d55c]335 "Failed to get USBLEGCTLSTS 2: %s.\n",
336 str_error(ret));
[50340bf]337 usb_log_debug("Zeroed USBLEGCTLSTS: %" PRIx32 ".\n",
[17d1542]338 usblegctlsts);
339 }
[40a5d40]340 }
341
342
[13927cf]343 /* Read again Legacy Support register */
[17d1542]344 ret = pci_read32(device, eecp + USBLEGSUP_OFFSET, &usblegsup);
[109d55c]345 CHECK_RET_RETURN(ret, "Failed to read USBLEGSUP: %s.\n", str_error(ret));
[50340bf]346 usb_log_debug("USBLEGSUP: %" PRIx32 ".\n", usblegsup);
[40a5d40]347
[67352d2]348 /*
349 * TURN OFF EHCI FOR NOW, DRIVER WILL REINITIALIZE IT
350 */
[40a5d40]351
[13927cf]352 /* Get size of capability registers in memory space. */
[17d1542]353 const unsigned operation_offset = *(uint8_t*)regs;
[40a5d40]354 usb_log_debug("USBCMD offset: %d.\n", operation_offset);
[13927cf]355
356 /* Zero USBCMD register. */
[40a5d40]357 volatile uint32_t *usbcmd =
[17d1542]358 (uint32_t*)((uint8_t*)regs + operation_offset + CMD_OFFSET);
[a948c23]359 volatile uint32_t *usbsts =
[17d1542]360 (uint32_t*)((uint8_t*)regs + operation_offset + STS_OFFSET);
361 volatile uint32_t *usbconf =
362 (uint32_t*)((uint8_t*)regs + operation_offset + CFG_OFFSET);
363 volatile uint32_t *usbint =
364 (uint32_t*)((uint8_t*)regs + operation_offset + INT_OFFSET);
[40a5d40]365 usb_log_debug("USBCMD value: %x.\n", *usbcmd);
366 if (*usbcmd & USBCMD_RUN) {
[17d1542]367 *usbsts = 0x3f; /* ack all interrupts */
368 *usbint = 0; /* disable all interrutps */
369 *usbconf = 0; /* relase control of RH ports */
[c060090]370
371 *usbcmd = 0;
372 /* Wait until hc is halted */
373 while ((*usbsts & USBSTS_HALTED) == 0);
[40a5d40]374 usb_log_info("EHCI turned off.\n");
375 } else {
376 usb_log_info("EHCI was not running.\n");
377 }
[17d1542]378 usb_log_debug("Registers: \n"
379 "\t USBCMD: %x(0x00080000 = at least 1ms between interrupts)\n"
380 "\t USBSTS: %x(0x00001000 = HC halted)\n"
381 "\t USBINT: %x(0x0 = no interrupts).\n"
382 "\t CONFIG: %x(0x0 = ports controlled by companion hc).\n",
383 *usbcmd, *usbsts, *usbint, *usbconf);
[40a5d40]384
[4ed80ce8]385 return ret;
[17d1542]386#undef CHECK_RET_RETURN
[40a5d40]387}
388
389/**
390 * @}
391 */
Note: See TracBrowser for help on using the repository browser.