00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
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
00241 void *regs = NULL;
00242 int ret = pio_enable((void*)reg_base, reg_size, ®s);
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
00251
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
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
00263
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
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
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
00290
00291
00292
00293
00294
00295 if ((usblegsup & 0xff) == 1) {
00296
00297
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
00306
00307 ret = pci_write32(device, eecp + USBLEGCTLSTS_OFFSET,
00308 0xe0000000);
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
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
00330
00331
00332
00333 const unsigned operation_offset = *(uint8_t*)regs;
00334 usb_log_debug("USBCMD offset: %d.\n", operation_offset);
00335
00336
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;
00348 *usbint = 0;
00349 *usbconf = 0;
00350
00351 *usbcmd = 0;
00352
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