📄 pcibr.c
字号:
void pcibr_provider_startup(devfs_handle_t);void pcibr_provider_shutdown(devfs_handle_t);int pcibr_reset(devfs_handle_t);pciio_endian_t pcibr_endian_set(devfs_handle_t, pciio_endian_t, pciio_endian_t);int pcibr_priority_bits_set(pcibr_soft_t, pciio_slot_t, pciio_priority_t);pciio_priority_t pcibr_priority_set(devfs_handle_t, pciio_priority_t);int pcibr_device_flags_set(devfs_handle_t, pcibr_device_flags_t);LOCAL cfg_p pcibr_config_addr(devfs_handle_t, unsigned);uint64_t pcibr_config_get(devfs_handle_t, unsigned, unsigned);LOCAL uint64_t do_pcibr_config_get(cfg_p, unsigned, unsigned);void pcibr_config_set(devfs_handle_t, unsigned, unsigned, uint64_t);LOCAL void do_pcibr_config_set(cfg_p, unsigned, unsigned, uint64_t);LOCAL pcibr_hints_t pcibr_hints_get(devfs_handle_t, int);void pcibr_hints_fix_rrbs(devfs_handle_t);void pcibr_hints_dualslot(devfs_handle_t, pciio_slot_t, pciio_slot_t);void pcibr_hints_intr_bits(devfs_handle_t, pcibr_intr_bits_f *);void pcibr_set_rrb_callback(devfs_handle_t, rrb_alloc_funct_t);void pcibr_hints_handsoff(devfs_handle_t);void pcibr_hints_subdevs(devfs_handle_t, pciio_slot_t, uint64_t);LOCAL int pcibr_slot_reset(devfs_handle_t,pciio_slot_t);LOCAL int pcibr_slot_info_init(devfs_handle_t,pciio_slot_t);LOCAL int pcibr_slot_info_free(devfs_handle_t,pciio_slot_t);LOCAL int pcibr_slot_addr_space_init(devfs_handle_t,pciio_slot_t);LOCAL int pcibr_slot_device_init(devfs_handle_t, pciio_slot_t);LOCAL int pcibr_slot_guest_info_init(devfs_handle_t,pciio_slot_t);LOCAL int pcibr_slot_initial_rrb_alloc(devfs_handle_t,pciio_slot_t);LOCAL int pcibr_slot_call_device_attach(devfs_handle_t,pciio_slot_t);LOCAL int pcibr_slot_call_device_detach(devfs_handle_t,pciio_slot_t);int pcibr_slot_powerup(devfs_handle_t,pciio_slot_t);int pcibr_slot_shutdown(devfs_handle_t,pciio_slot_t);int pcibr_slot_inquiry(devfs_handle_t,pciio_slot_t); /* ===================================================================== * RRB management */#define LSBIT(word) ((word) &~ ((word)-1))#define PCIBR_RRB_SLOT_VIRTUAL 8LOCAL voiddo_pcibr_rrb_clear(bridge_t *bridge, int rrb){ bridgereg_t status; /* bridge_lock must be held; * this RRB must be disabled. */ /* wait until RRB has no outstanduing XIO packets. */ while ((status = bridge->b_resp_status) & BRIDGE_RRB_INUSE(rrb)) { ; /* XXX- beats on bridge. bad idea? */ } /* if the RRB has data, drain it. */ if (status & BRIDGE_RRB_VALID(rrb)) { bridge->b_resp_clear = BRIDGE_RRB_CLEAR(rrb); /* wait until RRB is no longer valid. */ while ((status = bridge->b_resp_status) & BRIDGE_RRB_VALID(rrb)) { ; /* XXX- beats on bridge. bad idea? */ } }}LOCAL voiddo_pcibr_rrb_flush(bridge_t *bridge, int rrbn){ reg_p rrbp = &bridge->b_rrb_map[rrbn & 1].reg; bridgereg_t rrbv; int shft = 4 * (rrbn >> 1); unsigned ebit = BRIDGE_RRB_EN << shft; rrbv = *rrbp; if (rrbv & ebit) *rrbp = rrbv & ~ebit; do_pcibr_rrb_clear(bridge, rrbn); if (rrbv & ebit) *rrbp = rrbv;}/* * pcibr_rrb_count_valid: count how many RRBs are * marked valid for the specified PCI slot on this * bridge. * * NOTE: The "slot" parameter for all pcibr_rrb * management routines must include the "virtual" * bit; when manageing both the normal and the * virtual channel, separate calls to these * routines must be made. To denote the virtual * channel, add PCIBR_RRB_SLOT_VIRTUAL to the slot * number. * * IMPL NOTE: The obvious algorithm is to iterate * through the RRB fields, incrementing a count if * the RRB is valid and matches the slot. However, * it is much simpler to use an algorithm derived * from the "partitioned add" idea. First, XOR in a * pattern such that the fields that match this * slot come up "all ones" and all other fields * have zeros in the mismatching bits. Then AND * together the bits in the field, so we end up * with one bit turned on for each field that * matched. Now we need to count these bits. This * can be done either with a series of shift/add * instructions or by using "tmp % 15"; I expect * that the cascaded shift/add will be faster. */LOCAL intdo_pcibr_rrb_count_valid(bridge_t *bridge, pciio_slot_t slot){ bridgereg_t tmp; tmp = bridge->b_rrb_map[slot & 1].reg; tmp ^= 0x11111111 * (7 - slot / 2); tmp &= (0xCCCCCCCC & tmp) >> 2; tmp &= (0x22222222 & tmp) >> 1; tmp += tmp >> 4; tmp += tmp >> 8; tmp += tmp >> 16; return tmp & 15;}/* * do_pcibr_rrb_count_avail: count how many RRBs are * available to be allocated for the specified slot. * * IMPL NOTE: similar to the above, except we are * just counting how many fields have the valid bit * turned off. */LOCAL intdo_pcibr_rrb_count_avail(bridge_t *bridge, pciio_slot_t slot){ bridgereg_t tmp; tmp = bridge->b_rrb_map[slot & 1].reg; tmp = (0x88888888 & ~tmp) >> 3; tmp += tmp >> 4; tmp += tmp >> 8; tmp += tmp >> 16; return tmp & 15;}/* * do_pcibr_rrb_alloc: allocate some additional RRBs * for the specified slot. Returns -1 if there were * insufficient free RRBs to satisfy the request, * or 0 if the request was fulfilled. * * Note that if a request can be partially filled, * it will be, even if we return failure. * * IMPL NOTE: again we avoid iterating across all * the RRBs; instead, we form up a word containing * one bit for each free RRB, then peel the bits * off from the low end. */LOCAL intdo_pcibr_rrb_alloc(bridge_t *bridge, pciio_slot_t slot, int more){ int rv = 0; bridgereg_t reg, tmp, bit; reg = bridge->b_rrb_map[slot & 1].reg; tmp = (0x88888888 & ~reg) >> 3; while (more-- > 0) { bit = LSBIT(tmp); if (!bit) { rv = -1; break; } tmp &= ~bit; reg = ((reg & ~(bit * 15)) | (bit * (8 + slot / 2))); } bridge->b_rrb_map[slot & 1].reg = reg; return rv;}/* * do_pcibr_rrb_free: release some of the RRBs that * have been allocated for the specified * slot. Returns zero for success, or negative if * it was unable to free that many RRBs. * * IMPL NOTE: We form up a bit for each RRB * allocated to the slot, aligned with the VALID * bitfield this time; then we peel bits off one at * a time, releasing the corresponding RRB. */LOCAL intdo_pcibr_rrb_free(bridge_t *bridge, pciio_slot_t slot, int less){ int rv = 0; bridgereg_t reg, tmp, clr, bit; int i; clr = 0; reg = bridge->b_rrb_map[slot & 1].reg; /* This needs to be done otherwise the rrb's on the virtual channel * for this slot won't be freed !! */ tmp = reg & 0xbbbbbbbb; tmp ^= (0x11111111 * (7 - slot / 2)); tmp &= (0x33333333 & tmp) << 2; tmp &= (0x44444444 & tmp) << 1; while (less-- > 0) { bit = LSBIT(tmp); if (!bit) { rv = -1; break; } tmp &= ~bit; reg &= ~bit; clr |= bit; } bridge->b_rrb_map[slot & 1].reg = reg; for (i = 0; i < 8; i++) if (clr & (8 << (4 * i))) do_pcibr_rrb_clear(bridge, (2 * i) + (slot & 1)); return rv;}LOCAL voiddo_pcibr_rrb_autoalloc(pcibr_soft_t pcibr_soft, int slot, int more_rrbs){ bridge_t *bridge = pcibr_soft->bs_base; int got; for (got = 0; got < more_rrbs; ++got) { if (pcibr_soft->bs_rrb_res[slot & 7] > 0) pcibr_soft->bs_rrb_res[slot & 7]--; else if (pcibr_soft->bs_rrb_avail[slot & 1] > 0) pcibr_soft->bs_rrb_avail[slot & 1]--; else break; if (do_pcibr_rrb_alloc(bridge, slot, 1) < 0) break;#if PCIBR_RRB_DEBUG printk( "do_pcibr_rrb_autoalloc: add one to slot %d%s\n", slot & 7, slot & 8 ? "v" : "");#endif pcibr_soft->bs_rrb_valid[slot]++; }#if PCIBR_RRB_DEBUG printk("%s: %d+%d free RRBs. Allocation list:\n", pcibr_soft->bs_name, pcibr_soft->bs_rrb_avail[0], pcibr_soft->bs_rrb_avail[1]); for (slot = 0; slot < 8; ++slot) printk("\t%d+%d+%d", 0xFFF & pcibr_soft->bs_rrb_valid[slot], 0xFFF & pcibr_soft->bs_rrb_valid[slot + PCIBR_RRB_SLOT_VIRTUAL], pcibr_soft->bs_rrb_res[slot]); printk("\n");#endif}/* * Device driver interface to flush the write buffers for a specified * device hanging off the bridge. */intpcibr_wrb_flush(devfs_handle_t pconn_vhdl){ pciio_info_t pciio_info = pciio_info_get(pconn_vhdl); pciio_slot_t pciio_slot = pciio_info_slot_get(pciio_info); pcibr_soft_t pcibr_soft = (pcibr_soft_t) pciio_info_mfast_get(pciio_info); bridge_t *bridge = pcibr_soft->bs_base; volatile bridgereg_t *wrb_flush; wrb_flush = &(bridge->b_wr_req_buf[pciio_slot].reg); while (*wrb_flush); return(0);}/* * Device driver interface to request RRBs for a specified device * hanging off a Bridge. The driver requests the total number of * RRBs it would like for the normal channel (vchan0) and for the * "virtual channel" (vchan1). The actual number allocated to each * channel is returned. * * If we cannot allocate at least one RRB to a channel that needs * at least one, return -1 (failure). Otherwise, satisfy the request * as best we can and return 0. */intpcibr_rrb_alloc(devfs_handle_t pconn_vhdl, int *count_vchan0, int *count_vchan1){ pciio_info_t pciio_info = pciio_info_get(pconn_vhdl); pciio_slot_t pciio_slot = pciio_info_slot_get(pciio_info); pcibr_soft_t pcibr_soft = (pcibr_soft_t) pciio_info_mfast_get(pciio_info); bridge_t *bridge = pcibr_soft->bs_base; int desired_vchan0; int desired_vchan1; int orig_vchan0; int orig_vchan1; int delta_vchan0; int delta_vchan1; int final_vchan0; int final_vchan1; int avail_rrbs; unsigned s; int error; /* * TBD: temper request with admin info about RRB allocation, * and according to demand from other devices on this Bridge. * * One way of doing this would be to allocate two RRBs * for each device on the bus, before any drivers start * asking for extras. This has the weakness that one * driver might not give back an "extra" RRB until after * another driver has already failed to get one that * it wanted. */ s = pcibr_lock(pcibr_soft); /* How many RRBs do we own? */ orig_vchan0 = pcibr_soft->bs_rrb_valid[pciio_slot]; orig_vchan1 = pcibr_soft->bs_rrb_valid[pciio_slot + PCIBR_RRB_SLOT_VIRTUAL]; /* How many RRBs do we want? */ desired_vchan0 = count_vchan0 ? *count_vchan0 : orig_vchan0; desired_vchan1 = count_vchan1 ? *count_vchan1 : orig_vchan1; /* How many RRBs are free? */ avail_rrbs = pcibr_soft->bs_rrb_avail[pciio_slot & 1] + pcibr_soft->bs_rrb_res[pciio_slot]; /* Figure desired deltas */ delta_vchan0 = desired_vchan0 - orig_vchan0; delta_vchan1 = desired_vchan1 - orig_vchan1; /* Trim back deltas to something * that we can actually meet, by * decreasing the ending allocation * for whichever channel wants * more RRBs. If both want the same * number, cut the second channel. * NOTE: do not change the allocation for * a channel that was passed as NULL. */ while ((delta_vchan0 + delta_vchan1) > avail_rrbs) { if (count_vchan0 && (!count_vchan1 || ((orig_vchan0 + delta_vchan0) > (orig_vchan1 + delta_vchan1)))) delta_vchan0--; else delta_vchan1--; } /* Figure final RRB allocations */ final_vchan0 = orig_vchan0 + delta_vchan0; final_vchan1 = orig_vchan1 + delta_vchan1; /* If either channel wants RRBs but our actions * would leave it with none, declare an error, * but DO NOT change any RRB allocations. */ if ((desired_vchan0 && !final_vchan0) || (desired_vchan1 && !final_vchan1)) { error = -1; } else { /* Commit the allocations: free, then alloc. */ if (delta_vchan0 < 0) (void) do_pcibr_rrb_free(bridge, pciio_slot, -delta_vchan0); if (delta_vchan1 < 0) (void) do_pcibr_rrb_free(bridge, PCIBR_RRB_SLOT_VIRTUAL + pciio_slot, -delta_vchan1); if (delta_vchan0 > 0) (void) do_pcibr_rrb_alloc(bridge, pciio_slot, delta_vchan0); if (delta_vchan1 > 0) (void) do_pcibr_rrb_alloc(bridge, PCIBR_RRB_SLOT_VIRTUAL + pciio_slot, delta_vchan1); /* Return final values to caller. */ if (count_vchan0) *count_vchan0 = final_vchan0; if (count_vchan1) *count_vchan1 = final_vchan1; /* prevent automatic changes to this slot's RRBs */ pcibr_soft->bs_rrb_fixed |= 1 << pciio_slot; /* Track the actual allocations, release * any further reservations, and update the * number of available RRBs. */ pcibr_soft->bs_rrb_valid[pciio_slot] = final_vchan0; pcibr_soft->bs_rrb_valid[pciio_slot + PCIBR_RRB_SLOT_VIRTUAL] = final_vchan1; pcibr_soft->bs_rrb_avail[pciio_slot & 1] = pcibr_soft->bs_rrb_avail[pciio_slot & 1] + pcibr_soft->bs_rrb_res[pciio_slot] - delta_vchan0 - delta_vchan1; pcibr_soft->bs_rrb_res[pciio_slot] = 0;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -