pci.c

Go to the documentation of this file.
00001 /*
00002  * Copyright (c) 2011 Jan Vesely
00003  * All rights reserved.
00004  *
00005  * Redistribution and use in source and binary forms, with or without
00006  * modification, are permitted provided that the following conditions
00007  * are met:
00008  *
00009  * - Redistributions of source code must retain the above copyright
00010  *   notice, this list of conditions and the following disclaimer.
00011  * - Redistributions in binary form must reproduce the above copyright
00012  *   notice, this list of conditions and the following disclaimer in the
00013  *   documentation and/or other materials provided with the distribution.
00014  * - The name of the author may not be used to endorse or promote products
00015  *   derived from this software without specific prior written permission.
00016  *
00017  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
00018  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
00019  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
00020  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
00021  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
00022  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
00023  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
00024  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
00025  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
00026  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
00027  */
00036 #include <errno.h>
00037 #include <str_error.h>
00038 #include <assert.h>
00039 #include <as.h>
00040 #include <devman.h>
00041 #include <ddi.h>
00042 #include <libarch/ddi.h>
00043 #include <device/hw_res.h>
00044 
00045 #include <usb/debug.h>
00046 #include <pci_dev_iface.h>
00047 
00048 #include "pci.h"
00049 
00050 #define PAGE_SIZE_MASK 0xfffff000
00051 
00052 #define HCC_PARAMS_OFFSET 0x8
00053 #define HCC_PARAMS_EECP_MASK 0xff
00054 #define HCC_PARAMS_EECP_OFFSET 8
00055 
00056 #define CMD_OFFSET 0x0
00057 #define STS_OFFSET 0x4
00058 #define INT_OFFSET 0x8
00059 #define CFG_OFFSET 0x40
00060 
00061 #define USBCMD_RUN 1
00062 #define USBSTS_HALTED (1 << 12)
00063 
00064 #define USBLEGSUP_OFFSET 0
00065 #define USBLEGSUP_BIOS_CONTROL (1 << 16)
00066 #define USBLEGSUP_OS_CONTROL (1 << 24)
00067 #define USBLEGCTLSTS_OFFSET 4
00068 
00069 #define DEFAULT_WAIT 1000
00070 #define WAIT_STEP 10
00071 
00072 #define PCI_READ(size) \
00073 do { \
00074         const int parent_phone = \
00075             devman_parent_device_connect(dev->handle, IPC_FLAG_BLOCKING);\
00076         if (parent_phone < 0) {\
00077                 return parent_phone; \
00078         } \
00079         sysarg_t add = (sysarg_t)address; \
00080         sysarg_t val; \
00081         const int ret = \
00082             async_req_2_1(parent_phone, DEV_IFACE_ID(PCI_DEV_IFACE), \
00083                 IPC_M_CONFIG_SPACE_READ_##size, add, &val); \
00084         assert(value); \
00085         *value = val; \
00086         async_hangup(parent_phone); \
00087         return ret; \
00088 } while(0)
00089 
00090 static int pci_read32(const ddf_dev_t *dev, int address, uint32_t *value)
00091 {
00092         PCI_READ(32);
00093 }
00094 static int pci_read16(const ddf_dev_t *dev, int address, uint16_t *value)
00095 {
00096         PCI_READ(16);
00097 }
00098 static int pci_read8(const ddf_dev_t *dev, int address, uint8_t *value)
00099 {
00100         PCI_READ(8);
00101 }
00102 #define PCI_WRITE(size) \
00103 do { \
00104         const int parent_phone = \
00105             devman_parent_device_connect(dev->handle, IPC_FLAG_BLOCKING);\
00106         if (parent_phone < 0) {\
00107                 return parent_phone; \
00108         } \
00109         sysarg_t add = (sysarg_t)address; \
00110         sysarg_t val = value; \
00111         const int ret = \
00112             async_req_3_0(parent_phone, DEV_IFACE_ID(PCI_DEV_IFACE), \
00113                 IPC_M_CONFIG_SPACE_WRITE_##size, add, val); \
00114         async_hangup(parent_phone); \
00115         return ret; \
00116 } while(0)
00117 
00118 static int pci_write32(const ddf_dev_t *dev, int address, uint32_t value)
00119 {
00120         PCI_WRITE(32);
00121 }
00122 static int pci_write16(const ddf_dev_t *dev, int address, uint16_t value)
00123 {
00124         PCI_WRITE(16);
00125 }
00126 static int pci_write8(const ddf_dev_t *dev, int address, uint8_t value)
00127 {
00128         PCI_WRITE(8);
00129 }
00130 
00139 int pci_get_my_registers(const ddf_dev_t *dev,
00140     uintptr_t *mem_reg_address, size_t *mem_reg_size, int *irq_no)
00141 {
00142         assert(dev != NULL);
00143 
00144         const int parent_phone =
00145             devman_parent_device_connect(dev->handle, IPC_FLAG_BLOCKING);
00146         if (parent_phone < 0) {
00147                 return parent_phone;
00148         }
00149 
00150         int rc;
00151 
00152         hw_resource_list_t hw_resources;
00153         rc = hw_res_get_resource_list(parent_phone, &hw_resources);
00154         if (rc != EOK) {
00155                 async_hangup(parent_phone);
00156                 return rc;
00157         }
00158 
00159         uintptr_t mem_address = 0;
00160         size_t mem_size = 0;
00161         bool mem_found = false;
00162 
00163         int irq = 0;
00164         bool irq_found = false;
00165 
00166         size_t i;
00167         for (i = 0; i < hw_resources.count; i++) {
00168                 hw_resource_t *res = &hw_resources.resources[i];
00169                 switch (res->type)
00170                 {
00171                 case INTERRUPT:
00172                         irq = res->res.interrupt.irq;
00173                         irq_found = true;
00174                         usb_log_debug2("Found interrupt: %d.\n", irq);
00175                         break;
00176 
00177                 case MEM_RANGE:
00178                         if (res->res.mem_range.address != 0
00179                             && res->res.mem_range.size != 0 ) {
00180                                 mem_address = res->res.mem_range.address;
00181                                 mem_size = res->res.mem_range.size;
00182                                 usb_log_debug2("Found mem: %" PRIxn" %zu.\n",
00183                                     mem_address, mem_size);
00184                                 mem_found = true;
00185                                 }
00186                 default:
00187                         break;
00188                 }
00189         }
00190 
00191         if (mem_found && irq_found) {
00192                 *mem_reg_address = mem_address;
00193                 *mem_reg_size = mem_size;
00194                 *irq_no = irq;
00195                 rc = EOK;
00196         } else {
00197                 rc = ENOENT;
00198         }
00199 
00200         async_hangup(parent_phone);
00201         return rc;
00202 }
00203 /*----------------------------------------------------------------------------*/
00209 int pci_enable_interrupts(const ddf_dev_t *device)
00210 {
00211         const int parent_phone =
00212             devman_parent_device_connect(device->handle, IPC_FLAG_BLOCKING);
00213         if (parent_phone < 0) {
00214                 return parent_phone;
00215         }
00216         const bool enabled = hw_res_enable_interrupt(parent_phone);
00217         async_hangup(parent_phone);
00218         return enabled ? EOK : EIO;
00219 }
00220 /*----------------------------------------------------------------------------*/
00226 int pci_disable_legacy(
00227     const ddf_dev_t *device, uintptr_t reg_base, size_t reg_size, int irq)
00228 {
00229         assert(device);
00230         (void) pci_read16;
00231         (void) pci_read8;
00232         (void) pci_write16;
00233 
00234 #define CHECK_RET_RETURN(ret, message...) \
00235         if (ret != EOK) { \
00236                 usb_log_error(message); \
00237                 return ret; \
00238         } else (void)0
00239 
00240         /* Map EHCI registers */
00241         void *regs = NULL;
00242         int ret = pio_enable((void*)reg_base, reg_size, &regs);
00243         CHECK_RET_RETURN(ret, "Failed to map registers %p: %s.\n",
00244             (void *) reg_base, str_error(ret));
00245 
00246         const uint32_t hcc_params =
00247             *(uint32_t*)(regs + HCC_PARAMS_OFFSET);
00248         usb_log_debug("Value of hcc params register: %x.\n", hcc_params);
00249 
00250         /* Read value of EHCI Extended Capabilities Pointer
00251          * position of EEC registers (points to PCI config space) */
00252         const uint32_t eecp =
00253             (hcc_params >> HCC_PARAMS_EECP_OFFSET) & HCC_PARAMS_EECP_MASK;
00254         usb_log_debug("Value of EECP: %x.\n", eecp);
00255 
00256         /* Read the first EEC. i.e. Legacy Support register */
00257         uint32_t usblegsup;
00258         ret = pci_read32(device, eecp + USBLEGSUP_OFFSET, &usblegsup);
00259         CHECK_RET_RETURN(ret, "Failed to read USBLEGSUP: %s.\n", str_error(ret));
00260         usb_log_debug("USBLEGSUP: %" PRIx32 ".\n", usblegsup);
00261 
00262         /* Request control from firmware/BIOS, by writing 1 to highest byte.
00263          * (OS Control semaphore)*/
00264         usb_log_debug("Requesting OS control.\n");
00265         ret = pci_write8(device, eecp + USBLEGSUP_OFFSET + 3, 1);
00266         CHECK_RET_RETURN(ret, "Failed to request OS EHCI control: %s.\n",
00267             str_error(ret));
00268 
00269         size_t wait = 0;
00270         /* Wait for BIOS to release control. */
00271         ret = pci_read32(device, eecp + USBLEGSUP_OFFSET, &usblegsup);
00272         while ((wait < DEFAULT_WAIT) && (usblegsup & USBLEGSUP_BIOS_CONTROL)) {
00273                 async_usleep(WAIT_STEP);
00274                 ret = pci_read32(device, eecp + USBLEGSUP_OFFSET, &usblegsup);
00275                 wait += WAIT_STEP;
00276         }
00277 
00278 
00279         if ((usblegsup & USBLEGSUP_BIOS_CONTROL) == 0) {
00280                 usb_log_info("BIOS released control after %zu usec.\n", wait);
00281         } else {
00282                 /* BIOS failed to hand over control, this should not happen. */
00283                 usb_log_warning( "BIOS failed to release control after "
00284                     "%zu usecs, force it.\n", wait);
00285                 ret = pci_write32(device, eecp + USBLEGSUP_OFFSET,
00286                     USBLEGSUP_OS_CONTROL);
00287                 CHECK_RET_RETURN(ret, "Failed to force OS control: %s.\n",
00288                     str_error(ret));
00289                 /* Check capability type here, A value of 01h
00290                  * identifies the capability as Legacy Support.
00291                  * This extended capability requires one
00292                  * additional 32-bit register for control/status information,
00293                  * and this register is located at offset EECP+04h
00294                  * */
00295                 if ((usblegsup & 0xff) == 1) {
00296                         /* Read the second EEC
00297                          * Legacy Support and Control register */
00298                         uint32_t usblegctlsts;
00299                         ret = pci_read32(
00300                             device, eecp + USBLEGCTLSTS_OFFSET, &usblegctlsts);
00301                         CHECK_RET_RETURN(ret,
00302                             "Failed to get USBLEGCTLSTS: %s.\n", str_error(ret));
00303                         usb_log_debug("USBLEGCTLSTS: %" PRIx32 ".\n",
00304                             usblegctlsts);
00305                         /* Zero SMI enables in legacy control register.
00306                          * It should prevent pre-OS code from interfering. */
00307                         ret = pci_write32(device, eecp + USBLEGCTLSTS_OFFSET,
00308                             0xe0000000); /* three upper bits are WC */
00309                         CHECK_RET_RETURN(ret,
00310                             "Failed(%d) zero USBLEGCTLSTS.\n", ret);
00311                         udelay(10);
00312                         ret = pci_read32(
00313                             device, eecp + USBLEGCTLSTS_OFFSET, &usblegctlsts);
00314                         CHECK_RET_RETURN(ret,
00315                             "Failed to get USBLEGCTLSTS 2: %s.\n",
00316                             str_error(ret));
00317                         usb_log_debug("Zeroed USBLEGCTLSTS: %" PRIx32 ".\n",
00318                             usblegctlsts);
00319                 }
00320         }
00321 
00322 
00323         /* Read again Legacy Support register */
00324         ret = pci_read32(device, eecp + USBLEGSUP_OFFSET, &usblegsup);
00325         CHECK_RET_RETURN(ret, "Failed to read USBLEGSUP: %s.\n", str_error(ret));
00326         usb_log_debug("USBLEGSUP: %" PRIx32 ".\n", usblegsup);
00327 
00328         /*
00329          * TURN OFF EHCI FOR NOW, DRIVER WILL REINITIALIZE IT
00330          */
00331 
00332         /* Get size of capability registers in memory space. */
00333         const unsigned operation_offset = *(uint8_t*)regs;
00334         usb_log_debug("USBCMD offset: %d.\n", operation_offset);
00335 
00336         /* Zero USBCMD register. */
00337         volatile uint32_t *usbcmd =
00338             (uint32_t*)((uint8_t*)regs + operation_offset + CMD_OFFSET);
00339         volatile uint32_t *usbsts =
00340             (uint32_t*)((uint8_t*)regs + operation_offset + STS_OFFSET);
00341         volatile uint32_t *usbconf =
00342             (uint32_t*)((uint8_t*)regs + operation_offset + CFG_OFFSET);
00343         volatile uint32_t *usbint =
00344             (uint32_t*)((uint8_t*)regs + operation_offset + INT_OFFSET);
00345         usb_log_debug("USBCMD value: %x.\n", *usbcmd);
00346         if (*usbcmd & USBCMD_RUN) {
00347                 *usbsts = 0x3f; /* ack all interrupts */
00348                 *usbint = 0; /* disable all interrutps */
00349                 *usbconf = 0; /* relase control of RH ports */
00350 
00351                 *usbcmd = 0;
00352                 /* Wait until hc is halted */
00353                 while ((*usbsts & USBSTS_HALTED) == 0);
00354                 usb_log_info("EHCI turned off.\n");
00355         } else {
00356                 usb_log_info("EHCI was not running.\n");
00357         }
00358         usb_log_debug("Registers: \n"
00359             "\t USBCMD: %x(0x00080000 = at least 1ms between interrupts)\n"
00360             "\t USBSTS: %x(0x00001000 = HC halted)\n"
00361             "\t USBINT: %x(0x0 = no interrupts).\n"
00362             "\t CONFIG: %x(0x0 = ports controlled by companion hc).\n",
00363             *usbcmd, *usbsts, *usbint, *usbconf);
00364 
00365         return ret;
00366 #undef CHECK_RET_RETURN
00367 }
00368 /*----------------------------------------------------------------------------*/

Generated on Thu Jun 2 07:45:43 2011 for HelenOS/USB by  doxygen 1.4.7