Ignore:
File:
1 edited

Legend:

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

    r58563585 rf83666c  
    3434 */
    3535
    36 #include <assert.h>
    37 #include <async.h>
    3836#include <errno.h>
    39 #include <macros.h>
    40 #include <mem.h>
    41 #include <stdlib.h>
     37#include <stdbool.h>
    4238#include <str_error.h>
    43 #include <sys/types.h>
     39#include <adt/list.h>
     40#include <libarch/ddi.h>
    4441
    4542#include <usb/debug.h>
    4643#include <usb/usb.h>
    47 
     44#include <usb/ddfiface.h>
     45
     46#include "hc.h"
    4847#include "ohci_endpoint.h"
    49 #include "ohci_batch.h"
    50 
    51 #include "hc.h"
    5248
    5349#define OHCI_USED_INTERRUPTS \
     
    8884};
    8985
     86enum {
     87        /** Number of PIO ranges used in IRQ code */
     88        hc_irq_pio_range_count =
     89            sizeof(ohci_pio_ranges) / sizeof(irq_pio_range_t),
     90
     91        /** Number of commands used in IRQ code */
     92        hc_irq_cmd_count =
     93            sizeof(ohci_irq_commands) / sizeof(irq_cmd_t)
     94};
     95
    9096static void hc_gain_control(hc_t *instance);
    9197static void hc_start(hc_t *instance);
    9298static int hc_init_transfer_lists(hc_t *instance);
    9399static int hc_init_memory(hc_t *instance);
     100static int interrupt_emulator(hc_t *instance);
     101static int hc_schedule(hcd_t *hcd, usb_transfer_batch_t *batch);
    94102
    95103/** Generate IRQ code.
     
    98106 * @param[out] cmds Commands buffer.
    99107 * @param[in] cmds_size Size of the commands buffer (bytes).
    100  * @param[in] hw_res Device's resources.
     108 * @param[in] regs Physical address of device's registers.
     109 * @param[in] reg_size Size of the register area (bytes).
    101110 *
    102111 * @return Error code.
    103112 */
    104 int ohci_hc_gen_irq_code(irq_code_t *code, const hw_res_list_parsed_t *hw_res)
    105 {
    106         assert(code);
    107         assert(hw_res);
    108 
    109         if (hw_res->irqs.count != 1 || hw_res->mem_ranges.count != 1)
    110                 return EINVAL;
    111 
    112         const addr_range_t regs = hw_res->mem_ranges.ranges[0];
    113 
    114         if (RNGSZ(regs) < sizeof(ohci_regs_t))
     113int
     114hc_get_irq_code(irq_pio_range_t ranges[], size_t ranges_size, irq_cmd_t cmds[],
     115    size_t cmds_size, uintptr_t regs, size_t reg_size)
     116{
     117        if ((ranges_size < sizeof(ohci_pio_ranges)) ||
     118            (cmds_size < sizeof(ohci_irq_commands)) ||
     119            (reg_size < sizeof(ohci_regs_t)))
    115120                return EOVERFLOW;
    116121
    117         code->ranges = malloc(sizeof(ohci_pio_ranges));
    118         if (code->ranges == NULL)
    119                 return ENOMEM;
    120 
    121         code->cmds = malloc(sizeof(ohci_irq_commands));
    122         if (code->cmds == NULL) {
    123                 free(code->ranges);
    124                 return ENOMEM;
    125         }
    126 
    127         code->rangecount = ARRAY_SIZE(ohci_pio_ranges);
    128         code->cmdcount = ARRAY_SIZE(ohci_irq_commands);
    129 
    130         memcpy(code->ranges, ohci_pio_ranges, sizeof(ohci_pio_ranges));
    131         code->ranges[0].base = RNGABS(regs);
    132 
    133         memcpy(code->cmds, ohci_irq_commands, sizeof(ohci_irq_commands));
    134         ohci_regs_t *registers = (ohci_regs_t *) RNGABSPTR(regs);
    135         code->cmds[0].addr = (void *) &registers->interrupt_status;
    136         code->cmds[3].addr = (void *) &registers->interrupt_status;
    137         OHCI_WR(code->cmds[1].value, OHCI_USED_INTERRUPTS);
    138 
    139         usb_log_debug("Memory mapped regs at %p (size %zu), IRQ %d.\n",
    140             RNGABSPTR(regs), RNGSZ(regs), hw_res->irqs.irqs[0]);
    141 
    142         return hw_res->irqs.irqs[0];
     122        memcpy(ranges, ohci_pio_ranges, sizeof(ohci_pio_ranges));
     123        ranges[0].base = regs;
     124
     125        memcpy(cmds, ohci_irq_commands, sizeof(ohci_irq_commands));
     126        ohci_regs_t *registers = (ohci_regs_t *) regs;
     127        cmds[0].addr = (void *) &registers->interrupt_status;
     128        cmds[3].addr = (void *) &registers->interrupt_status;
     129        OHCI_WR(cmds[1].value, OHCI_USED_INTERRUPTS);
     130
     131        return EOK;
     132}
     133
     134/** Register interrupt handler.
     135 *
     136 * @param[in] device Host controller DDF device
     137 * @param[in] reg_base Register range base
     138 * @param[in] reg_size Register range size
     139 * @param[in] irq Interrupt number
     140 * @paran[in] handler Interrupt handler
     141 *
     142 * @return EOK on success or negative error code
     143 */
     144int hc_register_irq_handler(ddf_dev_t *device, uintptr_t reg_base, size_t reg_size,
     145    int irq, interrupt_handler_t handler)
     146{
     147        int rc;
     148
     149        irq_pio_range_t irq_ranges[hc_irq_pio_range_count];
     150        irq_cmd_t irq_cmds[hc_irq_cmd_count];
     151
     152        irq_code_t irq_code = {
     153                .rangecount = hc_irq_pio_range_count,
     154                .ranges = irq_ranges,
     155                .cmdcount = hc_irq_cmd_count,
     156                .cmds = irq_cmds
     157        };
     158
     159        rc = hc_get_irq_code(irq_ranges, sizeof(irq_ranges), irq_cmds,
     160            sizeof(irq_cmds), reg_base, reg_size);
     161        if (rc != EOK) {
     162                usb_log_error("Failed to generate IRQ code: %s.\n",
     163                    str_error(rc));
     164                return rc;
     165        }
     166
     167        /* Register handler to avoid interrupt lockup */
     168        rc = register_interrupt_handler(device, irq, handler, &irq_code);
     169        if (rc != EOK) {
     170                usb_log_error("Failed to register interrupt handler: %s.\n",
     171                    str_error(rc));
     172                return rc;
     173        }
     174
     175        return EOK;
     176}
     177
     178/** Announce OHCI root hub to the DDF
     179 *
     180 * @param[in] instance OHCI driver intance
     181 * @param[in] hub_fun DDF fuction representing OHCI root hub
     182 * @return Error code
     183 */
     184int hc_register_hub(hc_t *instance, ddf_fun_t *hub_fun)
     185{
     186        bool addr_reqd = false;
     187        bool ep_added = false;
     188        bool fun_bound = false;
     189        int rc;
     190
     191        assert(instance);
     192        assert(hub_fun);
     193
     194        /* Try to get address 1 for root hub. */
     195        instance->rh.address = 1;
     196        rc = usb_device_manager_request_address(
     197            &instance->generic.dev_manager, &instance->rh.address, false,
     198            USB_SPEED_FULL);
     199        if (rc != EOK) {
     200                usb_log_error("Failed to get OHCI root hub address: %s\n",
     201                    str_error(rc));
     202                goto error;
     203        }
     204
     205        addr_reqd = true;
     206
     207        rc = usb_endpoint_manager_add_ep(
     208            &instance->generic.ep_manager, instance->rh.address, 0,
     209            USB_DIRECTION_BOTH, USB_TRANSFER_CONTROL, USB_SPEED_FULL, 64,
     210            0, NULL, NULL);
     211        if (rc != EOK) {
     212                usb_log_error("Failed to register root hub control endpoint: %s.\n",
     213                    str_error(rc));
     214                goto error;
     215        }
     216
     217        ep_added = true;
     218
     219        rc = ddf_fun_add_match_id(hub_fun, "usb&class=hub", 100);
     220        if (rc != EOK) {
     221                usb_log_error("Failed to add root hub match-id: %s.\n",
     222                    str_error(rc));
     223                goto error;
     224        }
     225
     226        rc = ddf_fun_bind(hub_fun);
     227        if (rc != EOK) {
     228                usb_log_error("Failed to bind root hub function: %s.\n",
     229                    str_error(rc));
     230                goto error;
     231        }
     232
     233        fun_bound = true;
     234
     235        rc = usb_device_manager_bind_address(&instance->generic.dev_manager,
     236            instance->rh.address, ddf_fun_get_handle(hub_fun));
     237        if (rc != EOK) {
     238                usb_log_warning("Failed to bind root hub address: %s.\n",
     239                    str_error(rc));
     240        }
     241
     242        return EOK;
     243error:
     244        if (fun_bound)
     245                ddf_fun_unbind(hub_fun);
     246        if (ep_added) {
     247                usb_endpoint_manager_remove_ep(
     248                    &instance->generic.ep_manager, instance->rh.address, 0,
     249                    USB_DIRECTION_BOTH, NULL, NULL);
     250        }
     251        if (addr_reqd) {
     252                usb_device_manager_release_address(
     253                    &instance->generic.dev_manager, instance->rh.address);
     254        }
     255        return rc;
    143256}
    144257
     
    146259 *
    147260 * @param[in] instance Memory place for the structure.
    148  * @param[in] regs Device's resources
     261 * @param[in] regs Address of the memory mapped I/O registers.
     262 * @param[in] reg_size Size of the memory mapped area.
    149263 * @param[in] interrupts True if w interrupts should be used
    150264 * @return Error code
    151265 */
    152 int hc_init(hc_t *instance, const hw_res_list_parsed_t *hw_res, bool interrupts)
    153 {
    154         assert(instance);
    155         assert(hw_res);
    156         if (hw_res->mem_ranges.count != 1 ||
    157             hw_res->mem_ranges.ranges[0].size < sizeof(ohci_regs_t))
    158             return EINVAL;
    159 
    160         int ret = pio_enable_range(&hw_res->mem_ranges.ranges[0],
    161             (void **) &instance->registers);
    162         if (ret != EOK) {
    163                 usb_log_error("Failed to gain access to registers: %s.\n",
    164                     str_error(ret));
    165                 return ret;
    166         }
    167         usb_log_debug("Device registers at %" PRIx64 " (%zuB) accessible.\n",
    168             hw_res->mem_ranges.ranges[0].address.absolute,
    169             hw_res->mem_ranges.ranges[0].size);
     266int hc_init(hc_t *instance, uintptr_t regs, size_t reg_size, bool interrupts)
     267{
     268        assert(instance);
     269
     270        int rc =
     271            pio_enable((void*)regs, reg_size, (void**)&instance->registers);
     272        if (rc != EOK) {
     273                usb_log_error("Failed to gain access to device registers: %s.\n",
     274                    str_error(rc));
     275                return rc;
     276        }
    170277
    171278        list_initialize(&instance->pending_batches);
     279
     280        hcd_init(&instance->generic, USB_SPEED_FULL,
     281            BANDWIDTH_AVAILABLE_USB11, bandwidth_count_usb11);
     282        instance->generic.private_data = instance;
     283        instance->generic.schedule = hc_schedule;
     284        instance->generic.ep_add_hook = ohci_endpoint_init;
     285        instance->generic.ep_remove_hook = ohci_endpoint_fini;
     286
     287        rc = hc_init_memory(instance);
     288        if (rc != EOK) {
     289                usb_log_error("Failed to create OHCI memory structures: %s.\n",
     290                    str_error(rc));
     291                return rc;
     292        }
     293
    172294        fibril_mutex_initialize(&instance->guard);
    173         instance->hw_interrupts = interrupts;
    174 
    175         ret = hc_init_memory(instance);
    176         if (ret != EOK) {
    177                 usb_log_error("Failed to create OHCI memory structures: %s.\n",
    178                     str_error(ret));
    179                 // TODO: We should disable pio access here
    180                 return ret;
    181         }
    182295
    183296        hc_gain_control(instance);
    184297
    185         ohci_rh_init(&instance->rh, instance->registers, "ohci rh");
     298        if (!interrupts) {
     299                instance->interrupt_emulator =
     300                    fibril_create((int(*)(void*))interrupt_emulator, instance);
     301                fibril_add_ready(instance->interrupt_emulator);
     302        }
     303
     304        rh_init(&instance->rh, instance->registers);
    186305        hc_start(instance);
    187306
    188307        return EOK;
    189308}
    190 
    191 /** Safely dispose host controller internal structures
    192  *
    193  * @param[in] instance Host controller structure to use.
    194  */
    195 void hc_fini(hc_t *instance)
    196 {
    197         assert(instance);
    198         /* TODO: implement*/
    199 };
    200309
    201310void hc_enqueue_endpoint(hc_t *instance, const endpoint_t *ep)
     
    267376}
    268377
    269 int ohci_hc_status(hcd_t *hcd, uint32_t *status)
    270 {
    271         assert(hcd);
    272         assert(status);
    273         hc_t *instance = hcd_get_driver_data(hcd);
    274         assert(instance);
    275 
    276         if (instance->registers){
    277                 *status = OHCI_RD(instance->registers->interrupt_status);
    278                 OHCI_WR(instance->registers->interrupt_status, *status);
    279         }
    280         return EOK;
    281 }
    282 
    283378/** Add USB transfer to the schedule.
    284379 *
    285  * @param[in] hcd HCD driver structure.
     380 * @param[in] instance OHCI hc driver structure.
    286381 * @param[in] batch Batch representing the transfer.
    287382 * @return Error code.
    288383 */
    289 int ohci_hc_schedule(hcd_t *hcd, usb_transfer_batch_t *batch)
     384int hc_schedule(hcd_t *hcd, usb_transfer_batch_t *batch)
    290385{
    291386        assert(hcd);
    292         hc_t *instance = hcd_get_driver_data(hcd);
     387        hc_t *instance = hcd->private_data;
    293388        assert(instance);
    294389
    295390        /* Check for root hub communication */
    296         if (batch->ep->address == ohci_rh_get_address(&instance->rh)) {
     391        if (batch->ep->address == instance->rh.address) {
    297392                usb_log_debug("OHCI root hub request.\n");
    298                 return ohci_rh_schedule(&instance->rh, batch);
     393                rh_request(&instance->rh, batch);
     394                return EOK;
    299395        }
    300396        ohci_transfer_batch_t *ohci_batch = ohci_transfer_batch_get(batch);
     
    324420/** Interrupt handling routine
    325421 *
    326  * @param[in] hcd HCD driver structure.
     422 * @param[in] instance OHCI hc driver structure.
    327423 * @param[in] status Value of the status register at the time of interrupt.
    328424 */
    329 void ohci_hc_interrupt(hcd_t *hcd, uint32_t status)
    330 {
    331         assert(hcd);
    332         hc_t *instance = hcd_get_driver_data(hcd);
     425void hc_interrupt(hc_t *instance, uint32_t status)
     426{
    333427        status = OHCI_RD(status);
    334428        assert(instance);
     
    337431        usb_log_debug2("OHCI(%p) interrupt: %x.\n", instance, status);
    338432        if (status & I_RHSC)
    339                 ohci_rh_interrupt(&instance->rh);
     433                rh_interrupt(&instance->rh);
    340434
    341435        if (status & I_WDH) {
     
    368462        }
    369463
     464}
     465
     466/** Check status register regularly
     467 *
     468 * @param[in] instance OHCI hc driver structure.
     469 * @return Error code
     470 */
     471int interrupt_emulator(hc_t *instance)
     472{
     473        assert(instance);
     474        usb_log_info("Started interrupt emulator.\n");
     475        while (1) {
     476                const uint32_t status = instance->registers->interrupt_status;
     477                instance->registers->interrupt_status = status;
     478                hc_interrupt(instance, status);
     479                async_usleep(10000);
     480        }
     481        return EOK;
    370482}
    371483
     
    393505                    ohci_emulation_reg, OHCI_RD(*ohci_emulation_reg));
    394506                /* Zero everything but A20State */
    395                 // TODO: should we ack interrupts before doing this?
    396507                OHCI_CLR(*ohci_emulation_reg, ~0x100);
    397508                usb_log_debug(
     
    403514        if (OHCI_RD(instance->registers->control) & C_IR) {
    404515                usb_log_debug("SMM driver: request ownership change.\n");
    405                 // TODO: should we ack interrupts before doing this?
    406516                OHCI_SET(instance->registers->command_status, CS_OCR);
    407517                /* Hope that SMM actually knows its stuff or we can hang here */
    408                 while (OHCI_RD(instance->registers->control) & C_IR) {
     518                while (OHCI_RD(instance->registers->control & C_IR)) {
    409519                        async_usleep(1000);
    410520                }
     
    490600
    491601        /* Enable interrupts */
    492         if (instance->hw_interrupts) {
    493                 OHCI_WR(instance->registers->interrupt_enable,
    494                     OHCI_USED_INTERRUPTS);
    495                 usb_log_debug("Enabled interrupts: %x.\n",
    496                     OHCI_RD(instance->registers->interrupt_enable));
    497                 OHCI_WR(instance->registers->interrupt_enable, I_MI);
    498         }
     602        OHCI_WR(instance->registers->interrupt_enable, OHCI_USED_INTERRUPTS);
     603        usb_log_debug("Enabled interrupts: %x.\n",
     604            OHCI_RD(instance->registers->interrupt_enable));
     605        OHCI_WR(instance->registers->interrupt_enable, I_MI);
    499606
    500607        /* Set periodic start to 90% */
     
    522629do { \
    523630        const char *name = usb_str_transfer_type(type); \
    524         const int ret = endpoint_list_init(&instance->lists[type], name); \
     631        int ret = endpoint_list_init(&instance->lists[type], name); \
    525632        if (ret != EOK) { \
    526633                usb_log_error("Failed to setup %s endpoint list: %s.\n", \
Note: See TracChangeset for help on using the changeset viewer.