Changeset cb89430 in mainline for uspace/drv/bus/usb/xhci/hc.c


Ignore:
Timestamp:
2017-06-22T13:59:15Z (7 years ago)
Author:
Ondřej Hlavatý <aearsis@…>
Branches:
lfn, master, serial, ticket/834-toolchain-update, topic/msim-upgrade, topic/simplify-dev-export
Children:
e4d7363
Parents:
62ba2cbe
Message:

xhci: event rings && hc initialization (WIP)

File:
1 edited

Legend:

Unmodified
Added
Removed
  • uspace/drv/bus/usb/xhci/hc.c

    r62ba2cbe rcb89430  
    3535
    3636#include <errno.h>
     37#include <str_error.h>
    3738#include <usb/debug.h>
    3839#include <usb/host/ddf_helpers.h>
     40#include <usb/host/utils/malloc32.h>
    3941#include "debug.h"
    4042#include "hc.h"
    41 
    42 const ddf_hc_driver_t xhci_ddf_hc_driver = {
    43         .hc_speed = USB_SPEED_SUPER,
    44         .init = xhci_hc_init,
    45         .fini = xhci_hc_fini,
    46         .name = "XHCI-PCI",
    47         .ops = {
    48                 .schedule       = xhci_hc_schedule,
    49                 .irq_hook       = xhci_hc_interrupt,
    50                 .status_hook    = xhci_hc_status,
     43#include "hw_struct/trb.h"
     44
     45static const irq_cmd_t irq_commands[] = {
     46        {
     47                .cmd = CMD_PIO_READ_32,
     48                .dstarg = 1,
     49                .addr = NULL
     50        },
     51        {
     52                .cmd = CMD_AND,
     53                .srcarg = 1,
     54                .dstarg = 2,
     55                .value = 0
     56        },
     57        {
     58                .cmd = CMD_PREDICATE,
     59                .srcarg = 2,
     60                .value = 2
     61        },
     62        {
     63                .cmd = CMD_PIO_WRITE_A_32,
     64                .srcarg = 1,
     65                .addr = NULL
     66        },
     67        {
     68                .cmd = CMD_ACCEPT
    5169        }
    5270};
    5371
    54 int xhci_hc_init(hcd_t *hcd, const hw_res_list_parsed_t *hw_res, bool irq)
     72/**
     73 * Generates code to accept interrupts. The xHCI is designed primarily for
     74 * MSI/MSI-X, but we use PCI Interrupt Pin. In this mode, all the Interrupters
     75 * (except 0) are disabled.
     76 */
     77static int hc_gen_irq_code(irq_code_t *code, const hw_res_list_parsed_t *hw_res)
    5578{
    5679        int err;
    5780
     81        assert(code);
     82        assert(hw_res);
     83
     84        if (hw_res->irqs.count != 1 || hw_res->mem_ranges.count != 1) {
     85                usb_log_info("Unexpected HW resources to enable interrupts.");
     86                return EINVAL;
     87        }
     88
     89        addr_range_t mmio_range = hw_res->mem_ranges.ranges[0];
     90
     91        if (RNGSZ(mmio_range) < sizeof(xhci_cap_regs_t))
     92                return EOVERFLOW;
     93
     94
     95        xhci_cap_regs_t *cap_regs = NULL;
     96        if ((err = pio_enable_range(&mmio_range, (void **)&cap_regs)))
     97                return EIO;
     98
     99        code->ranges = malloc(sizeof(irq_pio_range_t));
     100        if (code->ranges == NULL)
     101                return ENOMEM;
     102
     103        code->cmds = malloc(sizeof(irq_commands));
     104        if (code->cmds == NULL) {
     105                free(code->ranges);
     106                return ENOMEM;
     107        }
     108
     109        code->rangecount = 1;
     110        code->ranges[0] = (irq_pio_range_t) {
     111            .base = RNGABS(mmio_range),
     112            .size = RNGSZ(mmio_range),
     113        };
     114
     115        code->cmdcount = ARRAY_SIZE(irq_commands);
     116        memcpy(code->cmds, irq_commands, sizeof(irq_commands));
     117
     118        void *intr0_iman = RNGABSPTR(mmio_range) + XHCI_REG_RD(cap_regs, XHCI_CAP_RTSOFF) + offsetof(xhci_rt_regs_t, ir[0]);
     119        code->cmds[0].addr = intr0_iman;
     120        code->cmds[3].addr = intr0_iman;
     121        code->cmds[1].value = host2xhci(32, 1);
     122
     123        return hw_res->irqs.irqs[0];
     124}
     125
     126static int hc_claim(ddf_dev_t *dev)
     127{
     128        // TODO: implement handoff: section 4.22.1
     129        return EOK;
     130}
     131
     132static int hc_reset(xhci_hc_t *hc)
     133{
     134        /* Stop the HC: set R/S to 0 */
     135        XHCI_REG_CLR(hc->op_regs, XHCI_OP_RS, 1);
     136
     137        /* Wait 16 ms until the HC is halted */
     138        async_usleep(16000);
     139        assert(XHCI_REG_RD(hc->op_regs, XHCI_OP_HCH));
     140
     141        /* Reset */
     142        XHCI_REG_SET(hc->op_regs, XHCI_OP_HCRST, 1);
     143
     144        /* Wait until the reset is complete */
     145        while (XHCI_REG_RD(hc->op_regs, XHCI_OP_HCRST))
     146                async_usleep(1000);
     147
     148        return EOK;
     149}
     150
     151/**
     152 * Initialize the HC: section 4.2
     153 */
     154static int hc_start(xhci_hc_t *hc, bool irq)
     155{
     156        int err;
     157
     158        if ((err = hc_reset(hc)))
     159                return err;
     160
     161        while (XHCI_REG_RD(hc->op_regs, XHCI_OP_CNR))
     162                async_usleep(1000);
     163
     164        uint64_t dcbaaptr = addr_to_phys(hc->event_ring.erst);
     165        XHCI_REG_WR(hc->op_regs, XHCI_OP_DCBAAP_LO, LOWER32(dcbaaptr));
     166        XHCI_REG_WR(hc->op_regs, XHCI_OP_DCBAAP_HI, UPPER32(dcbaaptr));
     167        XHCI_REG_WR(hc->op_regs, XHCI_OP_MAX_SLOTS_EN, 0);
     168
     169        uint64_t crptr = xhci_trb_ring_get_dequeue_ptr(&hc->command_ring);
     170        XHCI_REG_WR(hc->op_regs, XHCI_OP_CRCR_LO, LOWER32(crptr) >> 6);
     171        XHCI_REG_WR(hc->op_regs, XHCI_OP_CRCR_HI, UPPER32(crptr));
     172
     173        uint64_t erstptr = addr_to_phys(hc->event_ring.erst);
     174        xhci_interrupter_regs_t *intr0 = &hc->rt_regs->ir[0];
     175        XHCI_REG_WR(intr0, XHCI_INTR_ERSTSZ, hc->event_ring.segment_count);
     176        XHCI_REG_WR(intr0, XHCI_INTR_ERDP_LO, LOWER32(erstptr));
     177        XHCI_REG_WR(intr0, XHCI_INTR_ERDP_HI, UPPER32(erstptr));
     178        XHCI_REG_WR(intr0, XHCI_INTR_ERSTBA_LO, LOWER32(erstptr));
     179        XHCI_REG_WR(intr0, XHCI_INTR_ERSTBA_HI, UPPER32(erstptr));
     180
     181        // TODO: Setup scratchpad buffers
     182
     183        if (irq) {
     184                XHCI_REG_SET(intr0, XHCI_INTR_IE, 1);
     185                XHCI_REG_SET(hc->op_regs, XHCI_OP_INTE, 1);
     186        }
     187
     188        XHCI_REG_SET(hc->op_regs, XHCI_OP_RS, 1);
     189
     190        return EOK;
     191}
     192
     193static int hc_init(hcd_t *hcd, const hw_res_list_parsed_t *hw_res, bool irq)
     194{
     195        int err;
     196
    58197        assert(hcd);
     198        assert(hw_res);
    59199        assert(hcd_get_driver_data(hcd) == NULL);
    60200
     201        /* Initialize the MMIO ranges */
    61202        if (hw_res->mem_ranges.count != 1) {
    62                 usb_log_debug("Unexpected MMIO area, bailing out.");
     203                usb_log_error("Unexpected MMIO area, bailing out.");
    63204                return EINVAL;
    64205        }
     206
     207        addr_range_t mmio_range = hw_res->mem_ranges.ranges[0];
     208
     209        usb_log_debug("MMIO area at %p (size %zu), IRQ %d.\n",
     210            RNGABSPTR(mmio_range), RNGSZ(mmio_range), hw_res->irqs.irqs[0]);
     211
     212        if (RNGSZ(mmio_range) < sizeof(xhci_cap_regs_t))
     213                return EOVERFLOW;
     214
     215        void *base;
     216        if ((err = pio_enable_range(&mmio_range, &base)))
     217                return err;
    65218
    66219        xhci_hc_t *hc = malloc(sizeof(xhci_hc_t));
     
    68221                return ENOMEM;
    69222
    70         addr_range_t mmio_range = hw_res->mem_ranges.ranges[0];
    71 
    72         usb_log_debug("MMIO area at %p (size %zu), IRQ %d.\n",
    73             RNGABSPTR(mmio_range), RNGSZ(mmio_range), hw_res->irqs.irqs[0]);
    74 
    75         if ((err = pio_enable_range(&mmio_range, (void **)&hc->cap_regs)))
     223        hc->cap_regs = (xhci_cap_regs_t *)  base;
     224        hc->op_regs  = (xhci_op_regs_t *)  (base + XHCI_REG_RD(hc->cap_regs, XHCI_CAP_LENGTH));
     225        hc->rt_regs  = (xhci_rt_regs_t *)  (base + XHCI_REG_RD(hc->cap_regs, XHCI_CAP_RTSOFF));
     226        hc->db_arry  = (xhci_doorbell_t *) (base + XHCI_REG_RD(hc->cap_regs, XHCI_CAP_DBOFF));
     227
     228        usb_log_debug2("Initialized MMIO reg areas:");
     229        usb_log_debug2("\tCapability regs: %p", hc->cap_regs);
     230        usb_log_debug2("\tOperational regs: %p", hc->op_regs);
     231        usb_log_debug2("\tRuntime regs: %p", hc->rt_regs);
     232        usb_log_debug2("\tDoorbell array base: %p", hc->db_arry);
     233
     234        xhci_dump_cap_regs(hc->cap_regs);
     235
     236        hc->ac64 = XHCI_REG_RD(hc->cap_regs, XHCI_CAP_AC64);
     237        hc->max_slots = XHCI_REG_RD(hc->cap_regs, XHCI_CAP_MAX_SLOTS);
     238
     239        hc->dcbaa = malloc32((1 + hc->max_slots) * sizeof(xhci_device_ctx_t));
     240        if (!hc->dcbaa)
    76241                goto err_hc;
    77242
    78         xhci_dump_cap_regs(hc->cap_regs);
    79 
    80         uintptr_t base = (uintptr_t) hc->cap_regs;
    81 
    82         hc->op_regs = (xhci_op_regs_t *) (base + XHCI_REG_RD(hc->cap_regs, XHCI_CAP_LENGTH));
    83         hc->rt_regs = (xhci_rt_regs_t *) (base + XHCI_REG_RD(hc->cap_regs, XHCI_CAP_RTSOFF));
    84         hc->db_arry = (xhci_doorbell_t *) (base + XHCI_REG_RD(hc->cap_regs, XHCI_CAP_DBOFF));
    85 
    86         usb_log_debug("Initialized MMIO reg areas:");
    87         usb_log_debug("\tCapability regs: %p", hc->cap_regs);
    88         usb_log_debug("\tOperational regs: %p", hc->op_regs);
    89         usb_log_debug("\tRuntime regs: %p", hc->rt_regs);
    90         usb_log_debug("\tDoorbell array base: %p", hc->db_arry);
    91 
    92         xhci_dump_state(hc);
     243        if ((err = xhci_trb_ring_init(&hc->command_ring, hc)))
     244                goto err_dcbaa;
     245
     246        if ((err = xhci_event_ring_init(&hc->event_ring, hc)))
     247                goto err_cmd_ring;
     248
     249        // TODO: Allocate scratchpad buffers
    93250
    94251        hcd_set_implementation(hcd, hc, &xhci_ddf_hc_driver.ops);
    95252
    96         // TODO: check if everything fits into the mmio_area
    97 
    98         return EOK;
    99 
     253        if ((err = hc_start(hc, irq)))
     254                goto err_event_ring;
     255
     256        return EOK;
     257
     258err_event_ring:
     259        xhci_event_ring_fini(&hc->event_ring);
     260err_cmd_ring:
     261        xhci_trb_ring_fini(&hc->command_ring);
     262err_dcbaa:
     263        free32(hc->dcbaa);
    100264err_hc:
    101265        free(hc);
     266        hcd_set_implementation(hcd, NULL, NULL);
    102267        return err;
    103268}
    104269
    105 int xhci_hc_status(hcd_t *hcd, uint32_t *status)
    106 {
    107         usb_log_info("status");
    108         return ENOTSUP;
    109 }
    110 
    111 int xhci_hc_schedule(hcd_t *hcd, usb_transfer_batch_t *batch)
    112 {
    113         usb_log_info("schedule");
    114         return ENOTSUP;
    115 }
    116 
    117 void xhci_hc_interrupt(hcd_t *hcd, uint32_t status)
    118 {
    119         usb_log_info("Interrupted!");
    120 }
    121 
    122 void xhci_hc_fini(hcd_t *hcd)
    123 {
    124         assert(hcd);
     270static int hc_status(hcd_t *hcd, uint32_t *status)
     271{
     272        xhci_hc_t *hc = hcd_get_driver_data(hcd);
     273        assert(hc);
     274        assert(status);
     275
     276        *status = 0;
     277        if (hc->op_regs) {
     278                *status = XHCI_REG_RD(hc->op_regs, XHCI_OP_STATUS);
     279                XHCI_REG_WR(hc->op_regs, XHCI_OP_STATUS, *status & XHCI_STATUS_ACK_MASK);
     280        }
     281        usb_log_debug2("HC(%p): Read status: %x", hc, *status);
     282        return EOK;
     283}
     284
     285static int ring_doorbell(xhci_hc_t *hc, unsigned doorbell, unsigned target)
     286{
     287        uint32_t v = host2xhci(32, target & BIT_RRANGE(uint32_t, 7));
     288        pio_write_32(&hc->db_arry[doorbell], v);
     289        return EOK;
     290}
     291
     292static int send_no_op_command(xhci_hc_t *hc)
     293{
     294        xhci_trb_t trb;
     295        memset(&trb, 0, sizeof(trb));
     296
     297        trb.control = host2xhci(32, XHCI_TRB_TYPE_NO_OP_CMD << 10);
     298
     299        xhci_trb_ring_enqueue(&hc->command_ring, &trb);
     300        ring_doorbell(hc, 0, 0);
     301
     302        xhci_dump_trb(&trb);
     303        usb_log_debug2("HC(%p): Sent TRB", hc);
     304        return EOK;
     305}
     306
     307static int hc_schedule(hcd_t *hcd, usb_transfer_batch_t *batch)
     308{
     309        xhci_hc_t *hc = hcd_get_driver_data(hcd);
     310        assert(hc);
     311
     312        xhci_dump_state(hc);
     313        send_no_op_command(hc);
     314        async_usleep(1000);
     315        xhci_dump_state(hc);
     316
     317        xhci_dump_trb(hc->event_ring.dequeue_trb);
     318        return EOK;
     319}
     320
     321static void hc_run_event_ring(xhci_hc_t *hc, xhci_event_ring_t *event_ring, xhci_interrupter_regs_t *intr)
     322{
     323        int err;
     324        xhci_trb_t trb;
     325
     326        err = xhci_event_ring_dequeue(event_ring, &trb);;
     327
     328        switch (err) {
     329                case EOK:
     330                        usb_log_debug2("Dequeued from event ring.");
     331                        xhci_dump_trb(&trb);
     332                        break;
     333
     334                case ENOENT:
     335                        usb_log_debug2("Event ring finished.");
     336                        break;
     337
     338                default:
     339                        usb_log_warning("Error while accessing event ring: %s", str_error(err));
     340        }
     341
     342        /* Update the ERDP to make room inthe ring */
     343        uint64_t erstptr = addr_to_phys(hc->event_ring.erst);
     344        XHCI_REG_WR(intr, XHCI_INTR_ERDP_LO, LOWER32(erstptr));
     345        XHCI_REG_WR(intr, XHCI_INTR_ERDP_HI, UPPER32(erstptr));
     346}
     347
     348static void hc_interrupt(hcd_t *hcd, uint32_t status)
     349{
     350        xhci_hc_t *hc = hcd_get_driver_data(hcd);
     351        assert(hc);
     352
     353        if (status & XHCI_REG_MASK(XHCI_OP_HSE)) {
     354                usb_log_error("Host controller error occured. Bad things gonna happen...");
     355        }
     356
     357        if (status & XHCI_REG_MASK(XHCI_OP_EINT)) {
     358                usb_log_debug2("Event interrupt.");
     359
     360                xhci_interrupter_regs_t *intr0 = &hc->rt_regs->ir[0];
     361
     362                if (XHCI_REG_RD(intr0, XHCI_INTR_IP)) {
     363                        XHCI_REG_SET(intr0, XHCI_INTR_IP, 1);
     364                        hc_run_event_ring(hc, &hc->event_ring, intr0);
     365                }
     366        }
     367
     368        if (status & XHCI_REG_MASK(XHCI_OP_PCD)) {
     369                usb_log_error("Port change detected. Not implemented yet!");
     370        }
     371       
     372        if (status & XHCI_REG_MASK(XHCI_OP_SRE)) {
     373                usb_log_error("Save/Restore error occured. WTF, S/R mechanism not implemented!");
     374        }
     375}
     376
     377static void hc_fini(hcd_t *hcd)
     378{
     379        xhci_hc_t *hc = hcd_get_driver_data(hcd);
     380        assert(hc);
     381
    125382        usb_log_info("Finishing");
    126383
    127         xhci_hc_t *hc = hcd_get_driver_data(hcd);
     384        xhci_trb_ring_fini(&hc->command_ring);
     385        xhci_event_ring_fini(&hc->event_ring);
     386
    128387        free(hc);
    129388        hcd_set_implementation(hcd, NULL, NULL);
    130389}
    131390
     391const ddf_hc_driver_t xhci_ddf_hc_driver = {
     392        .hc_speed = USB_SPEED_SUPER,
     393        .irq_code_gen = hc_gen_irq_code,
     394        .claim = hc_claim,
     395        .init = hc_init,
     396        .fini = hc_fini,
     397        .name = "XHCI-PCI",
     398        .ops = {
     399                .schedule       = hc_schedule,
     400                .irq_hook       = hc_interrupt,
     401                .status_hook    = hc_status,
     402        }
     403};
     404
     405
    132406
    133407/**
Note: See TracChangeset for help on using the changeset viewer.