Ignore:
File:
1 edited

Legend:

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

    rb5111c46 rb5f813c  
    3434 */
    3535
     36#include <assert.h>
     37#include <async.h>
    3638#include <errno.h>
    37 #include <stdbool.h>
     39#include <macros.h>
     40#include <mem.h>
     41#include <stdlib.h>
    3842#include <str_error.h>
    39 #include <adt/list.h>
    40 #include <libarch/ddi.h>
     43#include <sys/types.h>
    4144
    4245#include <usb/debug.h>
    4346#include <usb/usb.h>
    44 #include <usb/ddfiface.h>
     47
     48#include "ohci_endpoint.h"
     49#include "ohci_batch.h"
    4550
    4651#include "hc.h"
    47 #include "ohci_endpoint.h"
    4852
    4953#define OHCI_USED_INTERRUPTS \
     
    8488};
    8589
    86 enum {
    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 
    9690static void hc_gain_control(hc_t *instance);
    9791static void hc_start(hc_t *instance);
    9892static int hc_init_transfer_lists(hc_t *instance);
    9993static int hc_init_memory(hc_t *instance);
    100 static int interrupt_emulator(hc_t *instance);
    101 static int hc_schedule(hcd_t *hcd, usb_transfer_batch_t *batch);
    10294
    10395/** Generate IRQ code.
     
    10698 * @param[out] cmds Commands buffer.
    10799 * @param[in] cmds_size Size of the commands buffer (bytes).
    108  * @param[in] regs Device's register range.
     100 * @param[in] hw_res Device's resources.
    109101 *
    110102 * @return Error code.
    111103 */
    112 int
    113 hc_get_irq_code(irq_pio_range_t ranges[], size_t ranges_size, irq_cmd_t cmds[],
    114     size_t cmds_size, addr_range_t *regs)
    115 {
    116         if ((ranges_size < sizeof(ohci_pio_ranges)) ||
    117             (cmds_size < sizeof(ohci_irq_commands)) ||
    118             (RNGSZ(*regs) < sizeof(ohci_regs_t)))
     104int 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))
    119115                return EOVERFLOW;
    120116
    121         memcpy(ranges, ohci_pio_ranges, sizeof(ohci_pio_ranges));
    122         ranges[0].base = RNGABS(*regs);
    123 
    124         memcpy(cmds, ohci_irq_commands, sizeof(ohci_irq_commands));
    125         ohci_regs_t *registers = (ohci_regs_t *) RNGABSPTR(*regs);
    126         cmds[0].addr = (void *) &registers->interrupt_status;
    127         cmds[3].addr = (void *) &registers->interrupt_status;
    128         OHCI_WR(cmds[1].value, OHCI_USED_INTERRUPTS);
    129 
    130         return EOK;
    131 }
    132 
    133 /** Register interrupt handler.
    134  *
    135  * @param[in] device Host controller DDF device
    136  * @param[in] regs Register range
    137  * @param[in] irq Interrupt number
    138  * @paran[in] handler Interrupt handler
    139  *
    140  * @return EOK on success or negative error code
    141  */
    142 int hc_register_irq_handler(ddf_dev_t *device, addr_range_t *regs, int irq,
    143     interrupt_handler_t handler)
    144 {
    145         int rc;
    146 
    147         irq_pio_range_t irq_ranges[hc_irq_pio_range_count];
    148         irq_cmd_t irq_cmds[hc_irq_cmd_count];
    149 
    150         irq_code_t irq_code = {
    151                 .rangecount = hc_irq_pio_range_count,
    152                 .ranges = irq_ranges,
    153                 .cmdcount = hc_irq_cmd_count,
    154                 .cmds = irq_cmds
    155         };
    156 
    157         rc = hc_get_irq_code(irq_ranges, sizeof(irq_ranges), irq_cmds,
    158             sizeof(irq_cmds), regs);
    159         if (rc != EOK) {
    160                 usb_log_error("Failed to generate IRQ code: %s.\n",
    161                     str_error(rc));
    162                 return rc;
    163         }
    164 
    165         /* Register handler to avoid interrupt lockup */
    166         rc = register_interrupt_handler(device, irq, handler, &irq_code);
    167         if (rc != EOK) {
    168                 usb_log_error("Failed to register interrupt handler: %s.\n",
    169                     str_error(rc));
    170                 return rc;
    171         }
    172 
    173         return EOK;
    174 }
    175 
    176 /** Announce OHCI root hub to the DDF
    177  *
    178  * @param[in] instance OHCI driver intance
    179  * @param[in] hub_fun DDF fuction representing OHCI root hub
    180  * @return Error code
    181  */
    182 int hc_register_hub(hc_t *instance, ddf_fun_t *hub_fun)
    183 {
    184         bool addr_reqd = false;
    185         bool ep_added = false;
    186         bool fun_bound = false;
    187         int rc;
    188 
    189         assert(instance);
    190         assert(hub_fun);
    191 
    192         /* Try to get address 1 for root hub. */
    193         instance->rh.address = 1;
    194         rc = usb_device_manager_request_address(
    195             &instance->generic->dev_manager, &instance->rh.address, false,
    196             USB_SPEED_FULL);
    197         if (rc != EOK) {
    198                 usb_log_error("Failed to get OHCI root hub address: %s\n",
    199                     str_error(rc));
    200                 goto error;
    201         }
    202 
    203         addr_reqd = true;
    204 
    205         rc = usb_endpoint_manager_add_ep(
    206             &instance->generic->ep_manager, instance->rh.address, 0,
    207             USB_DIRECTION_BOTH, USB_TRANSFER_CONTROL, USB_SPEED_FULL, 64,
    208             0, NULL, NULL);
    209         if (rc != EOK) {
    210                 usb_log_error("Failed to register root hub control endpoint: %s.\n",
    211                     str_error(rc));
    212                 goto error;
    213         }
    214 
    215         ep_added = true;
    216 
    217         rc = ddf_fun_add_match_id(hub_fun, "usb&class=hub", 100);
    218         if (rc != EOK) {
    219                 usb_log_error("Failed to add root hub match-id: %s.\n",
    220                     str_error(rc));
    221                 goto error;
    222         }
    223 
    224         rc = ddf_fun_bind(hub_fun);
    225         if (rc != EOK) {
    226                 usb_log_error("Failed to bind root hub function: %s.\n",
    227                     str_error(rc));
    228                 goto error;
    229         }
    230 
    231         fun_bound = true;
    232 
    233         rc = usb_device_manager_bind_address(&instance->generic->dev_manager,
    234             instance->rh.address, ddf_fun_get_handle(hub_fun));
    235         if (rc != EOK) {
    236                 usb_log_warning("Failed to bind root hub address: %s.\n",
    237                     str_error(rc));
    238         }
    239 
    240         return EOK;
    241 error:
    242         if (fun_bound)
    243                 ddf_fun_unbind(hub_fun);
    244         if (ep_added) {
    245                 usb_endpoint_manager_remove_ep(
    246                     &instance->generic->ep_manager, instance->rh.address, 0,
    247                     USB_DIRECTION_BOTH, NULL, NULL);
    248         }
    249         if (addr_reqd) {
    250                 usb_device_manager_release_address(
    251                     &instance->generic->dev_manager, instance->rh.address);
    252         }
    253         return rc;
     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];
    254143}
    255144
     
    257146 *
    258147 * @param[in] instance Memory place for the structure.
    259  * @param[in] HC function node
    260  * @param[in] regs Device's I/O registers range.
     148 * @param[in] regs Device's resources
    261149 * @param[in] interrupts True if w interrupts should be used
    262150 * @return Error code
    263151 */
    264 int hc_init(hc_t *instance, ddf_fun_t *fun, addr_range_t *regs, bool interrupts)
    265 {
    266         assert(instance);
    267 
    268         int rc = pio_enable_range(regs, (void **) &instance->registers);
    269         if (rc != EOK) {
    270                 usb_log_error("Failed to gain access to device registers: %s.\n",
    271                     str_error(rc));
    272                 return rc;
    273         }
     152int 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);
    274170
    275171        list_initialize(&instance->pending_batches);
    276 
    277         instance->generic = ddf_fun_data_alloc(fun, sizeof(hcd_t));
    278         if (instance->generic == NULL) {
    279                 usb_log_error("Out of memory.\n");
    280                 return ENOMEM;
    281         }
    282 
    283         hcd_init(instance->generic, USB_SPEED_FULL,
    284             BANDWIDTH_AVAILABLE_USB11, bandwidth_count_usb11);
    285         instance->generic->private_data = instance;
    286         instance->generic->schedule = hc_schedule;
    287         instance->generic->ep_add_hook = ohci_endpoint_init;
    288         instance->generic->ep_remove_hook = ohci_endpoint_fini;
    289 
    290         rc = hc_init_memory(instance);
    291         if (rc != EOK) {
     172        fibril_mutex_initialize(&instance->guard);
     173        instance->hw_interrupts = interrupts;
     174
     175        ret = hc_init_memory(instance);
     176        if (ret != EOK) {
    292177                usb_log_error("Failed to create OHCI memory structures: %s.\n",
    293                     str_error(rc));
    294                 return rc;
    295         }
    296 
    297         fibril_mutex_initialize(&instance->guard);
     178                    str_error(ret));
     179                //TODO: We should disable pio access here
     180                return ret;
     181        }
    298182
    299183        hc_gain_control(instance);
    300184
    301         if (!interrupts) {
    302                 instance->interrupt_emulator =
    303                     fibril_create((int(*)(void*))interrupt_emulator, instance);
    304                 fibril_add_ready(instance->interrupt_emulator);
    305         }
    306 
    307         rh_init(&instance->rh, instance->registers);
     185        ohci_rh_init(&instance->rh, instance->registers, "ohci rh");
    308186        hc_start(instance);
    309187
    310188        return EOK;
    311189}
     190
     191/** Safely dispose host controller internal structures
     192 *
     193 * @param[in] instance Host controller structure to use.
     194 */
     195void hc_fini(hc_t *instance)
     196{
     197        assert(instance);
     198        /* TODO: implement*/
     199};
    312200
    313201void hc_enqueue_endpoint(hc_t *instance, const endpoint_t *ep)
     
    379267}
    380268
     269int 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
    381283/** Add USB transfer to the schedule.
    382284 *
    383  * @param[in] instance OHCI hc driver structure.
     285 * @param[in] hcd HCD driver structure.
    384286 * @param[in] batch Batch representing the transfer.
    385287 * @return Error code.
    386288 */
    387 int hc_schedule(hcd_t *hcd, usb_transfer_batch_t *batch)
     289int ohci_hc_schedule(hcd_t *hcd, usb_transfer_batch_t *batch)
    388290{
    389291        assert(hcd);
    390         hc_t *instance = hcd->private_data;
     292        hc_t *instance = hcd_get_driver_data(hcd);
    391293        assert(instance);
    392294
    393295        /* Check for root hub communication */
    394         if (batch->ep->address == instance->rh.address) {
     296        if (batch->ep->address == ohci_rh_get_address(&instance->rh)) {
    395297                usb_log_debug("OHCI root hub request.\n");
    396                 rh_request(&instance->rh, batch);
    397                 return EOK;
     298                return ohci_rh_schedule(&instance->rh, batch);
    398299        }
    399300        ohci_transfer_batch_t *ohci_batch = ohci_transfer_batch_get(batch);
     
    423324/** Interrupt handling routine
    424325 *
    425  * @param[in] instance OHCI hc driver structure.
     326 * @param[in] hcd HCD driver structure.
    426327 * @param[in] status Value of the status register at the time of interrupt.
    427328 */
    428 void hc_interrupt(hc_t *instance, uint32_t status)
    429 {
     329void ohci_hc_interrupt(hcd_t *hcd, uint32_t status)
     330{
     331        assert(hcd);
     332        hc_t *instance = hcd_get_driver_data(hcd);
    430333        status = OHCI_RD(status);
    431334        assert(instance);
     
    434337        usb_log_debug2("OHCI(%p) interrupt: %x.\n", instance, status);
    435338        if (status & I_RHSC)
    436                 rh_interrupt(&instance->rh);
     339                ohci_rh_interrupt(&instance->rh);
    437340
    438341        if (status & I_WDH) {
     
    465368        }
    466369
    467 }
    468 
    469 /** Check status register regularly
    470  *
    471  * @param[in] instance OHCI hc driver structure.
    472  * @return Error code
    473  */
    474 int interrupt_emulator(hc_t *instance)
    475 {
    476         assert(instance);
    477         usb_log_info("Started interrupt emulator.\n");
    478         while (1) {
    479                 const uint32_t status = instance->registers->interrupt_status;
    480                 instance->registers->interrupt_status = status;
    481                 hc_interrupt(instance, status);
    482                 async_usleep(10000);
    483         }
    484         return EOK;
    485370}
    486371
     
    508393                    ohci_emulation_reg, OHCI_RD(*ohci_emulation_reg));
    509394                /* Zero everything but A20State */
     395                //TODO: should we ack interrupts before doing this?
    510396                OHCI_CLR(*ohci_emulation_reg, ~0x100);
    511397                usb_log_debug(
     
    517403        if (OHCI_RD(instance->registers->control) & C_IR) {
    518404                usb_log_debug("SMM driver: request ownership change.\n");
     405                //TODO: should we ack interrupts before doing this?
    519406                OHCI_SET(instance->registers->command_status, CS_OCR);
    520407                /* Hope that SMM actually knows its stuff or we can hang here */
    521                 while (OHCI_RD(instance->registers->control & C_IR)) {
     408                while (OHCI_RD(instance->registers->control) & C_IR) {
    522409                        async_usleep(1000);
    523410                }
     
    603490
    604491        /* Enable interrupts */
    605         OHCI_WR(instance->registers->interrupt_enable, OHCI_USED_INTERRUPTS);
    606         usb_log_debug("Enabled interrupts: %x.\n",
    607             OHCI_RD(instance->registers->interrupt_enable));
    608         OHCI_WR(instance->registers->interrupt_enable, I_MI);
     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        }
    609499
    610500        /* Set periodic start to 90% */
     
    632522do { \
    633523        const char *name = usb_str_transfer_type(type); \
    634         int ret = endpoint_list_init(&instance->lists[type], name); \
     524        const int ret = endpoint_list_init(&instance->lists[type], name); \
    635525        if (ret != EOK) { \
    636526                usb_log_error("Failed to setup %s endpoint list: %s.\n", \
Note: See TracChangeset for help on using the changeset viewer.