Changeset 26e7d6d in mainline for uspace/drv/bus/usb/uhci/hc.c


Ignore:
Timestamp:
2011-09-19T16:31:00Z (14 years ago)
Author:
Vojtech Horky <vojtechhorky@…>
Branches:
lfn, master, serial, ticket/834-toolchain-update, topic/msim-upgrade, topic/simplify-dev-export
Children:
a347a11
Parents:
3842a955 (diff), 086290d (diff)
Note: this is a merge changeset, the changes displayed below correspond to the merge itself.
Use the (diff) links above to see all the changes relative to each parent.
Message:

Merge mainline changes

File:
1 moved

Legend:

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

    r3842a955 r26e7d6d  
    4141
    4242#include "hc.h"
     43#include "uhci_batch.h"
    4344
    4445#define UHCI_INTR_ALLOW_INTERRUPTS \
     
    4849
    4950
     51static const irq_cmd_t uhci_irq_commands[] =
     52{
     53        { .cmd = CMD_PIO_READ_16, .dstarg = 1, .addr = NULL/*filled later*/},
     54        { .cmd = CMD_BTEST, .srcarg = 1, .dstarg = 2,
     55          .value = UHCI_STATUS_USED_INTERRUPTS | UHCI_STATUS_NM_INTERRUPTS },
     56        { .cmd = CMD_PREDICATE, .srcarg = 2, .value = 2 },
     57        { .cmd = CMD_PIO_WRITE_A_16, .srcarg = 1, .addr = NULL/*filled later*/},
     58        { .cmd = CMD_ACCEPT },
     59};
     60
     61static void hc_init_hw(const hc_t *instance);
     62static int hc_init_mem_structures(hc_t *instance);
    5063static int hc_init_transfer_lists(hc_t *instance);
    51 static int hc_init_mem_structures(hc_t *instance);
    52 static void hc_init_hw(hc_t *instance);
     64static int hc_schedule(hcd_t *hcd, usb_transfer_batch_t *batch);
    5365
    5466static int hc_interrupt_emulator(void *arg);
    5567static int hc_debug_checker(void *arg);
     68
     69/*----------------------------------------------------------------------------*/
     70/** Get number of commands used in IRQ code.
     71 * @return Number of commands.
     72 */
     73size_t hc_irq_cmd_count(void)
     74{
     75        return sizeof(uhci_irq_commands) / sizeof(irq_cmd_t);
     76}
     77/*----------------------------------------------------------------------------*/
     78/** Generate IRQ code commands.
     79 * @param[out] cmds Place to store the commands.
     80 * @param[in] cmd_size Size of the place (bytes).
     81 * @param[in] regs Physical address of device's registers.
     82 * @param[in] reg_size Size of the register area (bytes).
     83 *
     84 * @return Error code.
     85 */
     86int hc_get_irq_commands(
     87    irq_cmd_t cmds[], size_t cmd_size, uintptr_t regs, size_t reg_size)
     88{
     89        if (cmd_size < sizeof(uhci_irq_commands)
     90            || reg_size < sizeof(uhci_regs_t))
     91                return EOVERFLOW;
     92
     93        uhci_regs_t *registers = (uhci_regs_t*)regs;
     94
     95        memcpy(cmds, uhci_irq_commands, sizeof(uhci_irq_commands));
     96
     97        cmds[0].addr = (void*)&registers->usbsts;
     98        cmds[3].addr = (void*)&registers->usbsts;
     99        return EOK;
     100}
     101/*----------------------------------------------------------------------------*/
     102/** Take action based on the interrupt cause.
     103 *
     104 * @param[in] instance UHCI structure to use.
     105 * @param[in] status Value of the status register at the time of interrupt.
     106 *
     107 * Interrupt might indicate:
     108 * - transaction completed, either by triggering IOC, SPD, or an error
     109 * - some kind of device error
     110 * - resume from suspend state (not implemented)
     111 */
     112void hc_interrupt(hc_t *instance, uint16_t status)
     113{
     114        assert(instance);
     115        /* Lower 2 bits are transaction error and transaction complete */
     116        if (status & (UHCI_STATUS_INTERRUPT | UHCI_STATUS_ERROR_INTERRUPT)) {
     117                LIST_INITIALIZE(done);
     118                transfer_list_remove_finished(
     119                    &instance->transfers_interrupt, &done);
     120                transfer_list_remove_finished(
     121                    &instance->transfers_control_slow, &done);
     122                transfer_list_remove_finished(
     123                    &instance->transfers_control_full, &done);
     124                transfer_list_remove_finished(
     125                    &instance->transfers_bulk_full, &done);
     126
     127                while (!list_empty(&done)) {
     128                        link_t *item = list_first(&done);
     129                        list_remove(item);
     130                        uhci_transfer_batch_t *batch =
     131                            uhci_transfer_batch_from_link(item);
     132                        uhci_transfer_batch_call_dispose(batch);
     133                }
     134        }
     135        /* Resume interrupts are not supported */
     136        if (status & UHCI_STATUS_RESUME) {
     137                usb_log_error("Resume interrupt!\n");
     138        }
     139
     140        /* Bits 4 and 5 indicate hc error */
     141        if (status & (UHCI_STATUS_PROCESS_ERROR | UHCI_STATUS_SYSTEM_ERROR)) {
     142                usb_log_error("UHCI hardware failure!.\n");
     143                ++instance->hw_failures;
     144                transfer_list_abort_all(&instance->transfers_interrupt);
     145                transfer_list_abort_all(&instance->transfers_control_slow);
     146                transfer_list_abort_all(&instance->transfers_control_full);
     147                transfer_list_abort_all(&instance->transfers_bulk_full);
     148
     149                if (instance->hw_failures < UHCI_ALLOWED_HW_FAIL) {
     150                        /* reinitialize hw, this triggers virtual disconnect*/
     151                        hc_init_hw(instance);
     152                } else {
     153                        usb_log_fatal("Too many UHCI hardware failures!.\n");
     154                        hc_fini(instance);
     155                }
     156        }
     157}
    56158/*----------------------------------------------------------------------------*/
    57159/** Initialize UHCI hc driver structure
     
    69171int hc_init(hc_t *instance, void *regs, size_t reg_size, bool interrupts)
    70172{
    71         assert(reg_size >= sizeof(regs_t));
     173        assert(reg_size >= sizeof(uhci_regs_t));
    72174        int ret;
    73175
     
    82184
    83185        /* allow access to hc control registers */
    84         regs_t *io;
     186        uhci_regs_t *io;
    85187        ret = pio_enable(regs, reg_size, (void **)&io);
    86         CHECK_RET_RETURN(ret,
    87             "Failed(%d) to gain access to registers at %p: %s.\n",
    88             ret, io, str_error(ret));
     188        CHECK_RET_RETURN(ret, "Failed to gain access to registers at %p: %s.\n",
     189            io, str_error(ret));
    89190        instance->registers = io;
    90         usb_log_debug("Device registers at %p (%zuB) accessible.\n",
    91             io, reg_size);
     191        usb_log_debug(
     192            "Device registers at %p (%zuB) accessible.\n", io, reg_size);
     193
     194        ret = hcd_init(&instance->generic, BANDWIDTH_AVAILABLE_USB11,
     195            bandwidth_count_usb11);
     196        CHECK_RET_RETURN(ret, "Failed to initialize HCD generic driver: %s.\n",
     197            str_error(ret));
     198
     199        instance->generic.private_data = instance;
     200        instance->generic.schedule = hc_schedule;
     201        instance->generic.ep_add_hook = NULL;
     202
     203#undef CHECK_RET_DEST_FUN_RETURN
    92204
    93205        ret = hc_init_mem_structures(instance);
    94         CHECK_RET_RETURN(ret,
    95             "Failed(%d) to initialize UHCI memory structures: %s.\n",
    96             ret, str_error(ret));
     206        if (ret != EOK) {
     207                usb_log_error(
     208                    "Failed to initialize UHCI memory structures: %s.\n",
     209                    str_error(ret));
     210                hcd_destroy(&instance->generic);
     211                return ret;
     212        }
    97213
    98214        hc_init_hw(instance);
     
    105221
    106222        return EOK;
    107 #undef CHECK_RET_DEST_FUN_RETURN
    108223}
    109224/*----------------------------------------------------------------------------*/
     
    113228 * For magic values see UHCI Design Guide
    114229 */
    115 void hc_init_hw(hc_t *instance)
    116 {
    117         assert(instance);
    118         regs_t *registers = instance->registers;
     230void hc_init_hw(const hc_t *instance)
     231{
     232        assert(instance);
     233        uhci_regs_t *registers = instance->registers;
    119234
    120235        /* Reset everything, who knows what touched it before us */
    121236        pio_write_16(&registers->usbcmd, UHCI_CMD_GLOBAL_RESET);
    122         async_usleep(10000); /* 10ms according to USB spec */
     237        async_usleep(50000); /* 50ms according to USB spec(root hub reset) */
    123238        pio_write_16(&registers->usbcmd, 0);
    124239
    125         /* Reset hc, all states and counters */
     240        /* Reset hc, all states and counters. Hope that hw is not broken */
    126241        pio_write_16(&registers->usbcmd, UHCI_CMD_HCRESET);
    127242        do { async_usleep(10); }
     
    141256        }
    142257
    143         const uint16_t status = pio_read_16(&registers->usbcmd);
    144         if (status != 0)
    145                 usb_log_warning("Previous command value: %x.\n", status);
     258        const uint16_t cmd = pio_read_16(&registers->usbcmd);
     259        if (cmd != 0)
     260                usb_log_warning("Previous command value: %x.\n", cmd);
    146261
    147262        /* Start the hc with large(64B) packet FSBR */
     
    157272 *
    158273 * Structures:
    159  *  - interrupt code (I/O addressses are customized per instance)
    160274 *  - transfer lists (queue heads need to be accessible by the hw)
    161275 *  - frame list page (needs to be one UHCI hw accessible 4K page)
     
    164278{
    165279        assert(instance);
    166 #define CHECK_RET_RETURN(ret, message...) \
    167         if (ret != EOK) { \
    168                 usb_log_error(message); \
    169                 return ret; \
    170         } else (void) 0
    171 
    172         /* Init interrupt code */
    173         instance->interrupt_code.cmds = instance->interrupt_commands;
    174         {
    175                 /* Read status register */
    176                 instance->interrupt_commands[0].cmd = CMD_PIO_READ_16;
    177                 instance->interrupt_commands[0].dstarg = 1;
    178                 instance->interrupt_commands[0].addr =
    179                     &instance->registers->usbsts;
    180 
    181                 /* Test whether we are the interrupt cause */
    182                 instance->interrupt_commands[1].cmd = CMD_BTEST;
    183                 instance->interrupt_commands[1].value =
    184                     UHCI_STATUS_USED_INTERRUPTS | UHCI_STATUS_NM_INTERRUPTS;
    185                 instance->interrupt_commands[1].srcarg = 1;
    186                 instance->interrupt_commands[1].dstarg = 2;
    187 
    188                 /* Predicate cleaning and accepting */
    189                 instance->interrupt_commands[2].cmd = CMD_PREDICATE;
    190                 instance->interrupt_commands[2].value = 2;
    191                 instance->interrupt_commands[2].srcarg = 2;
    192 
    193                 /* Write clean status register */
    194                 instance->interrupt_commands[3].cmd = CMD_PIO_WRITE_A_16;
    195                 instance->interrupt_commands[3].srcarg = 1;
    196                 instance->interrupt_commands[3].addr =
    197                     &instance->registers->usbsts;
    198 
    199                 /* Accept interrupt */
    200                 instance->interrupt_commands[4].cmd = CMD_ACCEPT;
    201 
    202                 instance->interrupt_code.cmdcount = UHCI_NEEDED_IRQ_COMMANDS;
    203         }
     280
     281        /* Init USB frame list page */
     282        instance->frame_list = get_page();
     283        if (!instance->frame_list) {
     284                return ENOMEM;
     285        }
     286        usb_log_debug("Initialized frame list at %p.\n", instance->frame_list);
    204287
    205288        /* Init transfer lists */
    206289        int ret = hc_init_transfer_lists(instance);
    207         CHECK_RET_RETURN(ret, "Failed to init transfer lists.\n");
     290        if (ret != EOK) {
     291                usb_log_error("Failed to initialize transfer lists.\n");
     292                return_page(instance->frame_list);
     293                return ENOMEM;
     294        }
    208295        usb_log_debug("Initialized transfer lists.\n");
    209296
    210         /* Init USB frame list page*/
    211         instance->frame_list = get_page();
    212         ret = instance->frame_list ? EOK : ENOMEM;
    213         CHECK_RET_RETURN(ret, "Failed to get frame list page.\n");
    214         usb_log_debug("Initialized frame list at %p.\n", instance->frame_list);
    215297
    216298        /* Set all frames to point to the first queue head */
    217299        const uint32_t queue = LINK_POINTER_QH(
    218300                addr_to_phys(instance->transfers_interrupt.queue_head));
    219 
    220301        unsigned i = 0;
    221302        for(; i < UHCI_FRAME_LIST_COUNT; ++i) {
     
    223304        }
    224305
    225         /* Init device keeper */
    226         usb_device_keeper_init(&instance->manager);
    227         usb_log_debug("Initialized device manager.\n");
    228 
    229         ret = usb_endpoint_manager_init(&instance->ep_manager,
    230             BANDWIDTH_AVAILABLE_USB11);
    231         CHECK_RET_RETURN(ret, "Failed to initialize endpoint manager: %s.\n",
    232             str_error(ret));
    233 
    234         return EOK;
    235 #undef CHECK_RET_RETURN
     306        return EOK;
    236307}
    237308/*----------------------------------------------------------------------------*/
     
    252323        int ret = transfer_list_init(&instance->transfers_##type, name); \
    253324        if (ret != EOK) { \
    254                 usb_log_error("Failed(%d) to setup %s transfer list: %s.\n", \
    255                     ret, name, str_error(ret)); \
     325                usb_log_error("Failed to setup %s transfer list: %s.\n", \
     326                    name, str_error(ret)); \
    256327                transfer_list_fini(&instance->transfers_bulk_full); \
    257328                transfer_list_fini(&instance->transfers_control_full); \
     
    306377 * Checks for bandwidth availability and appends the batch to the proper queue.
    307378 */
    308 int hc_schedule(hc_t *instance, usb_transfer_batch_t *batch)
    309 {
     379int hc_schedule(hcd_t *hcd, usb_transfer_batch_t *batch)
     380{
     381        assert(hcd);
     382        hc_t *instance = hcd->private_data;
    310383        assert(instance);
    311384        assert(batch);
     385        uhci_transfer_batch_t *uhci_batch = uhci_transfer_batch_get(batch);
     386        if (!uhci_batch) {
     387                usb_log_error("Failed to create UHCI transfer structures.\n");
     388                return ENOMEM;
     389        }
    312390
    313391        transfer_list_t *list =
    314392            instance->transfers[batch->ep->speed][batch->ep->transfer_type];
    315393        assert(list);
    316         transfer_list_add_batch(list, batch);
    317 
    318         return EOK;
    319 }
    320 /*----------------------------------------------------------------------------*/
    321 /** Take action based on the interrupt cause.
    322  *
    323  * @param[in] instance UHCI structure to use.
    324  * @param[in] status Value of the status register at the time of interrupt.
    325  *
    326  * Interrupt might indicate:
    327  * - transaction completed, either by triggering IOC, SPD, or an error
    328  * - some kind of device error
    329  * - resume from suspend state (not implemented)
    330  */
    331 void hc_interrupt(hc_t *instance, uint16_t status)
    332 {
    333         assert(instance);
    334         /* Lower 2 bits are transaction error and transaction complete */
    335         if (status & (UHCI_STATUS_INTERRUPT | UHCI_STATUS_ERROR_INTERRUPT)) {
    336                 LIST_INITIALIZE(done);
    337                 transfer_list_remove_finished(
    338                     &instance->transfers_interrupt, &done);
    339                 transfer_list_remove_finished(
    340                     &instance->transfers_control_slow, &done);
    341                 transfer_list_remove_finished(
    342                     &instance->transfers_control_full, &done);
    343                 transfer_list_remove_finished(
    344                     &instance->transfers_bulk_full, &done);
    345 
    346                 while (!list_empty(&done)) {
    347                         link_t *item = list_first(&done);
    348                         list_remove(item);
    349                         usb_transfer_batch_t *batch =
    350                             list_get_instance(item, usb_transfer_batch_t, link);
    351                         usb_transfer_batch_finish(batch);
    352                 }
    353         }
    354         /* Resume interrupts are not supported */
    355         if (status & UHCI_STATUS_RESUME) {
    356                 usb_log_error("Resume interrupt!\n");
    357         }
    358 
    359         /* Bits 4 and 5 indicate hc error */
    360         if (status & (UHCI_STATUS_PROCESS_ERROR | UHCI_STATUS_SYSTEM_ERROR)) {
    361                 usb_log_error("UHCI hardware failure!.\n");
    362                 ++instance->hw_failures;
    363                 transfer_list_abort_all(&instance->transfers_interrupt);
    364                 transfer_list_abort_all(&instance->transfers_control_slow);
    365                 transfer_list_abort_all(&instance->transfers_control_full);
    366                 transfer_list_abort_all(&instance->transfers_bulk_full);
    367 
    368                 if (instance->hw_failures < UHCI_ALLOWED_HW_FAIL) {
    369                         /* reinitialize hw, this triggers virtual disconnect*/
    370                         hc_init_hw(instance);
    371                 } else {
    372                         usb_log_fatal("Too many UHCI hardware failures!.\n");
    373                         hc_fini(instance);
    374                 }
    375         }
     394        transfer_list_add_batch(list, uhci_batch);
     395
     396        return EOK;
    376397}
    377398/*----------------------------------------------------------------------------*/
     
    393414                if (status != 0)
    394415                        usb_log_debug2("UHCI status: %x.\n", status);
    395 // Qemu fails to report stalled communication
    396 // see https://bugs.launchpad.net/qemu/+bug/757654
    397 // This is a simple workaround to force queue processing every time
    398         //      status |= 1;
    399416                hc_interrupt(instance, status);
    400417                async_usleep(UHCI_INT_EMULATOR_TIMEOUT);
     
    402419        return EOK;
    403420}
    404 /*---------------------------------------------------------------------------*/
     421/*----------------------------------------------------------------------------*/
    405422/** Debug function, checks consistency of memory structures.
    406423 *
Note: See TracChangeset for help on using the changeset viewer.