staticconststruct ath10k_pci_supp_chip ath10k_pci_supp_chips[] = { /* QCA988X pre 2.0 chips are not supported because they need some nasty *hacks.ath10kdoesn'thavethemandthesedevicescrashhorribly *becauseofthat.
*/
{ QCA988X_2_0_DEVICE_ID_UBNT, QCA988X_HW_2_0_CHIP_ID_REV },
{ QCA988X_2_0_DEVICE_ID, QCA988X_HW_2_0_CHIP_ID_REV },
staticint ath10k_pci_wake_wait(struct ath10k *ar)
{ int tot_delay = 0; int curr_delay = 5;
while (tot_delay < PCIE_WAKE_TIMEOUT) { if (ath10k_pci_is_awake(ar)) { if (tot_delay > PCIE_WAKE_LATE_US)
ath10k_warn(ar, "device wakeup took %d ms which is unusually long, otherwise it works normally.\n",
tot_delay / 1000); return0;
}
udelay(curr_delay);
tot_delay += curr_delay;
if (curr_delay < 50)
curr_delay += 5;
}
return -ETIMEDOUT;
}
staticint ath10k_pci_force_wake(struct ath10k *ar)
{ struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); unsignedlong flags; int ret = 0;
if (ar_pci->pci_ps) return ret;
spin_lock_irqsave(&ar_pci->ps_lock, flags);
if (!ar_pci->ps_awake) {
iowrite32(PCIE_SOC_WAKE_V_MASK,
ar_pci->mem + PCIE_LOCAL_BASE_ADDRESS +
PCIE_SOC_WAKE_ADDRESS);
ret = ath10k_pci_wake_wait(ar); if (ret == 0)
ar_pci->ps_awake = true;
}
/* This function can be called very frequently. To avoid excessive *CPUstallsforMMIOreadsuseacachevartoholdthedevicestate.
*/ if (!ar_pci->ps_awake) {
__ath10k_pci_wake(ar);
ret = ath10k_pci_wake_wait(ar); if (ret == 0)
ar_pci->ps_awake = true;
}
if (ret == 0) {
ar_pci->ps_wake_refcount++;
WARN_ON(ar_pci->ps_wake_refcount == 0);
}
/* Check if the shared legacy irq is for us */
cause = ath10k_pci_read32(ar, SOC_CORE_BASE_ADDRESS +
PCIE_INTR_CAUSE_ADDRESS); if (cause & (PCIE_INTR_FIRMWARE_MASK | PCIE_INTR_CE_MASK_ALL)) returntrue;
returnfalse;
}
void ath10k_pci_disable_and_clear_intx_irq(struct ath10k *ar)
{ /* IMPORTANT: INTR_CLR register has to be set after *INTR_ENABLEissetto0,otherwiseinterruptcannotbe *reallycleared.
*/
ath10k_pci_write32(ar, SOC_CORE_BASE_ADDRESS + PCIE_INTR_ENABLE_ADDRESS, 0);
ath10k_pci_write32(ar, SOC_CORE_BASE_ADDRESS + PCIE_INTR_CLR_ADDRESS,
PCIE_INTR_FIRMWARE_MASK | PCIE_INTR_CE_MASK_ALL);
/* IMPORTANT: this extra read transaction is required to *flushthepostedwritebuffer.
*/
(void)ath10k_pci_read32(ar, SOC_CORE_BASE_ADDRESS +
PCIE_INTR_ENABLE_ADDRESS);
}
/* IMPORTANT: this extra read transaction is required to *flushthepostedwritebuffer.
*/
(void)ath10k_pci_read32(ar, SOC_CORE_BASE_ADDRESS +
PCIE_INTR_ENABLE_ADDRESS);
}
data_buf = dma_alloc_coherent(ar->dev, alloc_nbytes, &ce_data_base,
GFP_ATOMIC); if (!data_buf) {
ret = -ENOMEM; goto done;
}
/* The address supplied by the caller is in the *TargetCPUvirtualaddressspace. * *InordertousethisaddresswiththediagnosticCE, *convertitfromTargetCPUvirtualaddressspace *toCEaddressspace
*/
address = ath10k_pci_targ_cpu_to_ce_addr(ar, address);
ret = ath10k_ce_rx_post_buf(ce_diag, &ce_data, ce_data); if (ret != 0) goto done;
/* Request CE to send from Target(!) address to Host buffer */
ret = ath10k_ce_send(ce_diag, NULL, (u32)address, nbytes, 0, 0); if (ret) goto done;
i = 0; while (ath10k_ce_completed_send_next(ce_diag, NULL) != 0) {
udelay(DIAG_ACCESS_CE_WAIT_US);
i += DIAG_ACCESS_CE_WAIT_US;
if (i > DIAG_ACCESS_CE_TIMEOUT_US) {
ret = -EBUSY; goto done;
}
}
i = 0; while (ath10k_ce_completed_recv_next(ce_diag, (void **)&buf,
&completed_nbytes) != 0) {
udelay(DIAG_ACCESS_CE_WAIT_US);
i += DIAG_ACCESS_CE_WAIT_US;
if (i > DIAG_ACCESS_CE_TIMEOUT_US) {
ret = -EBUSY; goto done;
}
}
if (nbytes != completed_nbytes) {
ret = -EIO; goto done;
}
ret = ath10k_pci_diag_read32(ar, host_addr, &addr); if (ret != 0) {
ath10k_warn(ar, "failed to get memcpy hi address for firmware address %d: %d\n",
src, ret); return ret;
}
ret = ath10k_pci_diag_read_mem(ar, addr, dest, len); if (ret != 0) {
ath10k_warn(ar, "failed to memcpy firmware memory from %d (%d B): %d\n",
addr, len, ret); return ret;
}
/* Copy caller's data to allocated DMA buf */
memcpy(data_buf, data, nbytes);
/* Set up to receive directly into Target(!) address */
ret = ath10k_ce_rx_post_buf(ce_diag, &address, address); if (ret != 0) goto done;
/* *RequestCEtosendcaller-supplieddatathat *wascopiedtobouncebuffertoTarget(!)address.
*/
ret = ath10k_ce_send(ce_diag, NULL, ce_data_base, nbytes, 0, 0); if (ret != 0) goto done;
i = 0; while (ath10k_ce_completed_send_next(ce_diag, NULL) != 0) {
udelay(DIAG_ACCESS_CE_WAIT_US);
i += DIAG_ACCESS_CE_WAIT_US;
if (i > DIAG_ACCESS_CE_TIMEOUT_US) {
ret = -EBUSY; goto done;
}
}
i = 0; while (ath10k_ce_completed_recv_next(ce_diag, (void **)&buf,
&completed_nbytes) != 0) {
udelay(DIAG_ACCESS_CE_WAIT_US);
i += DIAG_ACCESS_CE_WAIT_US;
if (i > DIAG_ACCESS_CE_TIMEOUT_US) {
ret = -EBUSY; goto done;
}
}
if (nbytes != completed_nbytes) {
ret = -EIO; goto done;
}
if (*buf != address) {
ret = -EIO; goto done;
}
remaining_bytes -= nbytes;
address += nbytes;
data += nbytes;
}
done: if (data_buf) {
dma_free_coherent(ar->dev, alloc_nbytes, data_buf,
ce_data_base);
}
if (ret != 0)
ath10k_warn(ar, "failed to write diag value at 0x%x: %d\n",
address, ret);
/* Called by lower (CE) layer when a send to Target completes. */ staticvoid ath10k_pci_htc_tx_cb(struct ath10k_ce_pipe *ce_state)
{ struct ath10k *ar = ce_state->ar; struct sk_buff_head list; struct sk_buff *skb;
__skb_queue_head_init(&list); while (ath10k_ce_completed_send_next(ce_state, (void **)&skb) == 0) { /* no need to call tx completion for NULL pointers */ if (skb == NULL) continue;
__skb_queue_tail(&list, skb);
}
while ((skb = __skb_dequeue(&list)))
ath10k_htc_tx_completion_handler(ar, skb);
}
if (unlikely(max_nbytes < nbytes)) {
ath10k_warn(ar, "rxed more than expected (nbytes %d, max %d)",
nbytes, max_nbytes);
dev_kfree_skb_any(skb); continue;
}
/* No need to acquire ce_lock for CE5, since this is the only place CE5 *isprocessedotherthaninitanddeinit.BeforereleasingCE5 *buffers,interruptsaredisabled.ThusCE5accessisserialized.
*/
__skb_queue_head_init(&list); while (ath10k_ce_completed_recv_next_nolock(ce_state, &transfer_context,
&nbytes) == 0) {
skb = transfer_context;
max_nbytes = skb->len + skb_tailroom(skb);
if (unlikely(max_nbytes < nbytes)) {
ath10k_warn(ar, "rxed more than expected (nbytes %d, max %d)",
nbytes, max_nbytes); continue;
}
/*let device gain the buffer again*/
dma_sync_single_for_device(ar->dev, ATH10K_SKB_RXCB(skb)->paddr,
skb->len + skb_tailroom(skb),
DMA_FROM_DEVICE);
}
ath10k_ce_rx_update_write_idx(ce_pipe, nentries);
}
/* Called by lower (CE) layer when data is received from the Target. */ staticvoid ath10k_pci_htc_rx_cb(struct ath10k_ce_pipe *ce_state)
{
ath10k_pci_process_rx_cb(ce_state, ath10k_htc_rx_completion_handler);
}
staticvoid ath10k_pci_htt_htc_rx_cb(struct ath10k_ce_pipe *ce_state)
{ /* CE4 polling needs to be done whenever CE pipe which transports *HTTRx(target->host)isprocessed.
*/
ath10k_ce_per_engine_service(ce_state->ar, 4);
/* Called by lower (CE) layer when data is received from the Target. *Only10.4firmwareusesseparateCEtotransferpktlogdata.
*/ staticvoid ath10k_pci_pktlog_rx_cb(struct ath10k_ce_pipe *ce_state)
{
ath10k_pci_process_rx_cb(ce_state,
ath10k_htt_rx_pktlog_completion_handler);
}
/* Called by lower (CE) layer when a send to HTT Target completes. */ staticvoid ath10k_pci_htt_tx_cb(struct ath10k_ce_pipe *ce_state)
{ struct ath10k *ar = ce_state->ar; struct sk_buff *skb;
while (ath10k_ce_completed_send_next(ce_state, (void **)&skb) == 0) { /* no need to call tx completion for NULL pointers */ if (!skb) continue;
/* Called by lower (CE) layer when HTT data is received from the Target. */ staticvoid ath10k_pci_htt_rx_cb(struct ath10k_ce_pipe *ce_state)
{ /* CE4 polling needs to be done whenever CE pipe which transports *HTTRx(target->host)isprocessed.
*/
ath10k_ce_per_engine_service(ce_state->ar, 4);
/* fill the gap between the first register section and register *startaddress
*/ for (i = 0; i < skip_size; i++) {
*buf = ATH10K_MAGIC_NOT_COPIED;
buf++;
}
count = 0;
for (i = 0; cur_section != NULL; i++) {
section_size = cur_section->end - cur_section->start;
if (section_size <= 0) {
ath10k_warn(ar, "incorrect ramdump format with start address 0x%x and stop address 0x%x\n",
cur_section->start,
cur_section->end); break;
}
if (cur_section->end > next_section->start) {
ath10k_warn(ar, "next ramdump section 0x%x is smaller than current end address 0x%x\n",
next_section->start,
cur_section->end); break;
}
if (buf_len < (skip_size + section_size)) {
ath10k_warn(ar, "ramdump buffer is too small: %zu\n", buf_len); break;
}
buf_len -= skip_size + section_size;
/* read section to dest memory */
ret = ath10k_pci_diag_read_mem(ar, cur_section->start,
buf, section_size); if (ret) {
ath10k_warn(ar, "failed to read ramdump from section 0x%x: %d\n",
cur_section->start, ret); break;
}
buf += section_size;
count += section_size;
/* fill in the gap between this section and the next */ for (j = 0; j < skip_size; j++) {
*buf = ATH10K_MAGIC_NOT_COPIED;
buf++;
}
count += skip_size;
if (!next_section) /* this was the last section */ break;
val = ath10k_pci_read32(ar, SOC_CORE_BASE_ADDRESS +
FW_RAM_CONFIG_ADDRESS); if (val != config) {
ath10k_warn(ar, "failed to set RAM config from 0x%x to 0x%x\n",
val, config); return -EIO;
}
for (i = 0; i < region->len; i += 4) {
iowrite32(base_addr + i, ar_pci->mem + QCA99X0_CPU_MEM_ADDR_REG);
*(u32 *)(buf + i) = ioread32(ar_pci->mem + QCA99X0_CPU_MEM_DATA_REG);
}
return region->len;
}
/* if an error happened returns < 0, otherwise the length */ staticint ath10k_pci_dump_memory_reg(struct ath10k *ar, conststruct ath10k_mem_region *region,
u8 *buf)
{ struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
u32 i; int ret;
mutex_lock(&ar->conf_mutex); if (ar->state != ATH10K_STATE_ON) {
ath10k_warn(ar, "Skipping pci_dump_memory_reg invalid state\n");
ret = -EIO; goto done;
}
for (i = 0; i < region->len; i += 4)
*(u32 *)(buf + i) = ioread32(ar_pci->mem + region->start + i);
ret = region->len;
done:
mutex_unlock(&ar->conf_mutex); return ret;
}
/* if an error happened returns < 0, otherwise the length */ staticint ath10k_pci_dump_memory_generic(struct ath10k *ar, conststruct ath10k_mem_region *current_region,
u8 *buf)
{ int ret;
if (current_region->section_table.size > 0) /* Copy each section individually. */ return ath10k_pci_dump_memory_section(ar,
current_region,
buf,
current_region->len);
/* No individual memory sections defined so we can *copytheentirememoryregion.
*/
ret = ath10k_pci_diag_read_mem(ar,
current_region->start,
buf,
current_region->len); if (ret) {
ath10k_warn(ar, "failed to copy ramdump region %s: %d\n",
current_region->name, ret); return ret;
}
for (i = 0; i < mem_layout->region_table.size; i++) {
count = 0;
if (current_region->len > buf_len) {
ath10k_warn(ar, "memory region %s size %d is larger that remaining ramdump buffer size %zu\n",
current_region->name,
current_region->len,
buf_len); break;
}
/* To get IRAM dump, the host driver needs to switch target *ramconfigfromDRAMtoIRAM.
*/ if (current_region->type == ATH10K_MEM_REGION_TYPE_IRAM1 ||
current_region->type == ATH10K_MEM_REGION_TYPE_IRAM2) {
shift = current_region->start >> 20;
ret = ath10k_pci_set_ram_config(ar, shift); if (ret) {
ath10k_warn(ar, "failed to switch ram config to IRAM for section %s: %d\n",
current_region->name, ret); break;
}
}
/* Reserve space for the header. */
hdr = (void *)buf;
buf += sizeof(*hdr);
buf_len -= sizeof(*hdr);
switch (current_region->type) { case ATH10K_MEM_REGION_TYPE_IOSRAM:
count = ath10k_pci_dump_memory_sram(ar, current_region, buf); break; case ATH10K_MEM_REGION_TYPE_IOREG:
ret = ath10k_pci_dump_memory_reg(ar, current_region, buf); if (ret < 0) break;
count = ret; break; default:
ret = ath10k_pci_dump_memory_generic(ar, current_region, buf); if (ret < 0) break;
switch (ar->hw_rev) { case ATH10K_HW_QCA988X: case ATH10K_HW_QCA9887: case ATH10K_HW_QCA6174: case ATH10K_HW_QCA9377:
val = ath10k_pci_read32(ar, SOC_CORE_BASE_ADDRESS +
CORE_CTRL_ADDRESS);
val &= ~CORE_CTRL_PCIE_REG_31_MASK;
ath10k_pci_write32(ar, SOC_CORE_BASE_ADDRESS +
CORE_CTRL_ADDRESS, val); break; case ATH10K_HW_QCA99X0: case ATH10K_HW_QCA9984: case ATH10K_HW_QCA9888: case ATH10K_HW_QCA4019: /* TODO: Find appropriate register configuration for QCA99X0 *tomaskirq/MSI.
*/ break; case ATH10K_HW_WCN3990: break;
}
}
switch (ar->hw_rev) { case ATH10K_HW_QCA988X: case ATH10K_HW_QCA9887: case ATH10K_HW_QCA6174: case ATH10K_HW_QCA9377:
val = ath10k_pci_read32(ar, SOC_CORE_BASE_ADDRESS +
CORE_CTRL_ADDRESS);
val |= CORE_CTRL_PCIE_REG_31_MASK;
ath10k_pci_write32(ar, SOC_CORE_BASE_ADDRESS +
CORE_CTRL_ADDRESS, val); break; case ATH10K_HW_QCA99X0: case ATH10K_HW_QCA9984: case ATH10K_HW_QCA9888: case ATH10K_HW_QCA4019: /* TODO: Find appropriate register configuration for QCA99X0 *tounmaskirq/MSI.
*/ break; case ATH10K_HW_WCN3990: break;
}
}
/* Most likely the device has HTT Rx ring configured. The only way to *preventthedevicefromaccessing(andpossiblecorrupting)host *memoryistoresetthechipnow. * *There'salsonoknownwayofmaskingMSIinterruptsonthedevice. *ForrangedMSItheCE-relatedinterruptscanbemasked.However *regardlesshowmanyMSIinterruptsareassignedthefirstone *isalwaysusedforfirmwareindications(crashes)andcannotbe *masked.Topreventthedevicefromassertingtheinterruptresetit *beforeproceedingwithcleanup.
*/
ath10k_pci_safe_chip_reset(ar);
switch (ar_pci->pdev->device) { case QCA988X_2_0_DEVICE_ID_UBNT: case QCA988X_2_0_DEVICE_ID: case QCA99X0_2_0_DEVICE_ID: case QCA9888_2_0_DEVICE_ID: case QCA9984_1_0_DEVICE_ID: case QCA9887_1_0_DEVICE_ID: return1; case QCA6164_2_1_DEVICE_ID: case QCA6174_2_1_DEVICE_ID: switch (MS(ar->bus_param.chip_id, SOC_CHIP_ID_REV)) { case QCA6174_HW_1_0_CHIP_ID_REV: case QCA6174_HW_1_1_CHIP_ID_REV: case QCA6174_HW_2_1_CHIP_ID_REV: case QCA6174_HW_2_2_CHIP_ID_REV: return3; case QCA6174_HW_1_3_CHIP_ID_REV: return2; case QCA6174_HW_3_0_CHIP_ID_REV: case QCA6174_HW_3_1_CHIP_ID_REV: case QCA6174_HW_3_2_CHIP_ID_REV: return9;
} break; case QCA9377_1_0_DEVICE_ID: return9;
}
ath10k_warn(ar, "unknown number of banks, assuming 1\n"); return1;
}
/* Download to Target the CE Config and the service-to-CE map */
interconnect_targ_addr =
host_interest_item_address(HI_ITEM(hi_interconnect_state));
/* Supply Target-side CE configuration */
ret = ath10k_pci_diag_read32(ar, interconnect_targ_addr,
&pcie_state_targ_addr); if (ret != 0) {
ath10k_err(ar, "Failed to get pcie state addr: %d\n", ret); return ret;
}
if (pcie_state_targ_addr == 0) {
ret = -EIO;
ath10k_err(ar, "Invalid pcie state addr\n"); return ret;
}
ret = ath10k_pci_diag_read32(ar, (pcie_state_targ_addr +
offsetof(struct pcie_state,
pipe_cfg_addr)),
&pipe_cfg_targ_addr); if (ret != 0) {
ath10k_err(ar, "Failed to get pipe cfg addr: %d\n", ret); return ret;
}
if (pipe_cfg_targ_addr == 0) {
ret = -EIO;
ath10k_err(ar, "Invalid pipe cfg addr\n"); return ret;
}
ret = ath10k_pci_diag_write_mem(ar, pipe_cfg_targ_addr,
ar_pci->pipe_config, sizeof(struct ce_pipe_config) *
NUM_TARGET_CE_CONFIG_WLAN);
if (ret != 0) {
ath10k_err(ar, "Failed to write pipe cfg: %d\n", ret); return ret;
}
ret = ath10k_pci_diag_read32(ar, (pcie_state_targ_addr +
offsetof(struct pcie_state,
svc_to_pipe_map)),
&svc_to_pipe_map); if (ret != 0) {
ath10k_err(ar, "Failed to get svc/pipe map: %d\n", ret); return ret;
}
if (svc_to_pipe_map == 0) {
ret = -EIO;
ath10k_err(ar, "Invalid svc_to_pipe map\n"); return ret;
}
ret = ath10k_pci_diag_write_mem(ar, svc_to_pipe_map,
ar_pci->serv_to_pipe, sizeof(pci_target_service_to_ce_map_wlan)); if (ret != 0) {
ath10k_err(ar, "Failed to write svc/pipe map: %d\n", ret); return ret;
}
ret = ath10k_pci_diag_read32(ar, (pcie_state_targ_addr +
offsetof(struct pcie_state,
config_flags)),
&pcie_config_flags); if (ret != 0) {
ath10k_err(ar, "Failed to get pcie config_flags: %d\n", ret); return ret;
}
pcie_config_flags &= ~PCIE_CONFIG_FLAG_ENABLE_L1;
ret = ath10k_pci_diag_write32(ar, (pcie_state_targ_addr +
offsetof(struct pcie_state,
config_flags)),
pcie_config_flags); if (ret != 0) {
ath10k_err(ar, "Failed to write pcie config_flags: %d\n", ret); return ret;
}
/* configure early allocation */
ealloc_targ_addr = host_interest_item_address(HI_ITEM(hi_early_alloc));
ret = ath10k_pci_diag_read32(ar, ealloc_targ_addr, &ealloc_value); if (ret != 0) {
ath10k_err(ar, "Failed to get early alloc val: %d\n", ret); return ret;
}
/* first bank is switched to IRAM */
ealloc_value |= ((HI_EARLY_ALLOC_MAGIC << HI_EARLY_ALLOC_MAGIC_SHIFT) &
HI_EARLY_ALLOC_MAGIC_MASK);
ealloc_value |= ((ath10k_bus_get_num_banks(ar) <<
HI_EARLY_ALLOC_IRAM_BANKS_SHIFT) &
HI_EARLY_ALLOC_IRAM_BANKS_MASK);
ret = ath10k_pci_diag_write32(ar, ealloc_targ_addr, ealloc_value); if (ret != 0) {
ath10k_err(ar, "Failed to set early alloc val: %d\n", ret); return ret;
}
/* Tell Target to proceed with initialization */
flag2_targ_addr = host_interest_item_address(HI_ITEM(hi_option_flag2));
ret = ath10k_pci_diag_read32(ar, flag2_targ_addr, &flag2_value); if (ret != 0) {
ath10k_err(ar, "Failed to get option val: %d\n", ret); return ret;
}
flag2_value |= HI_OPTION_EARLY_CFG_DONE;
ret = ath10k_pci_diag_write32(ar, flag2_targ_addr, flag2_value); if (ret != 0) {
ath10k_err(ar, "Failed to set option val: %d\n", ret); return ret;
}
void ath10k_pci_free_pipes(struct ath10k *ar)
{ int i;
for (i = 0; i < CE_COUNT; i++)
ath10k_ce_free_pipe(ar, i);
}
int ath10k_pci_init_pipes(struct ath10k *ar)
{ struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); int i, ret;
for (i = 0; i < CE_COUNT; i++) {
ret = ath10k_ce_init_pipe(ar, i, &ar_pci->attr[i]); if (ret) {
ath10k_err(ar, "failed to initialize copy engine pipe %d: %d\n",
i, ret); return ret;
}
}
val = ath10k_pci_read32(ar, FW_INDICATOR_ADDRESS); return (val == 0xffffffff);
}
/* this function effectively clears target memory controller assert line */ staticvoid ath10k_pci_warm_reset_si0(struct ath10k *ar)
{
u32 val;
val = ath10k_pci_soc_read32(ar, SOC_RESET_CONTROL_ADDRESS);
ath10k_pci_soc_write32(ar, SOC_RESET_CONTROL_ADDRESS,
val | SOC_RESET_CONTROL_SI0_RST_MASK);
val = ath10k_pci_soc_read32(ar, SOC_RESET_CONTROL_ADDRESS);
msleep(10);
val = ath10k_pci_soc_read32(ar, SOC_RESET_CONTROL_ADDRESS);
ath10k_pci_soc_write32(ar, SOC_RESET_CONTROL_ADDRESS,
val & ~SOC_RESET_CONTROL_SI0_RST_MASK);
val = ath10k_pci_soc_read32(ar, SOC_RESET_CONTROL_ADDRESS);
val = ath10k_pci_soc_read32(ar, SOC_RESET_CONTROL_ADDRESS);
ath10k_pci_soc_write32(ar, SOC_RESET_CONTROL_ADDRESS,
val | SOC_RESET_CONTROL_CPU_WARM_RST_MASK);
}
val = ath10k_pci_soc_read32(ar, SOC_LF_TIMER_CONTROL0_ADDRESS);
ath10k_pci_soc_write32(ar, SOC_LF_TIMER_CONTROL0_ADDRESS,
val & ~SOC_LF_TIMER_CONTROL0_ENABLE_MASK);
}
staticint ath10k_pci_warm_reset(struct ath10k *ar)
{ int ret;
/* Make sure the target CPU is not doing anything dangerous, e.g. if it *weretoaccesscopyenginewhilehostperformscopyenginereset *thenitispossibleforthedevicetoconfusepci-econtrollerto *thepointofbringinghostsystemtoacompletestop(i.e.hang).
*/
ath10k_pci_warm_reset_si0(ar);
ath10k_pci_warm_reset_cpu(ar);
ath10k_pci_init_pipes(ar);
ath10k_pci_wait_for_target_init(ar);
/* Some hardware revisions (e.g. CUS223v2) has issues with cold reset. *Itisthuspreferredtousewarmresetwhichissaferbutmaynotbe *abletorecoverthedevicefromallpossiblefailscenarios. * *Warmresetdoesn'talwaysworkonfirsttrysoattemptitafew *timesbeforegivingup.
*/ for (i = 0; i < ATH10K_PCI_NUM_WARM_RESET_ATTEMPTS; i++) {
ret = ath10k_pci_warm_reset(ar); if (ret) {
ath10k_warn(ar, "failed to warm reset attempt %d of %d: %d\n",
i + 1, ATH10K_PCI_NUM_WARM_RESET_ATTEMPTS,
ret); continue;
}
/* FIXME: Sometimes copy engine doesn't recover after warm *reset.Inmostcasesthisneedscoldreset.Insomeofthese *casesthedeviceisinsuchastatethatacoldresetmay *lockupthehost. * *Readinganyhostinterestregisterviacopyengineis *sufficienttoverifyifdeviceiscapableofbooting *firmwareblob.
*/
ret = ath10k_pci_init_pipes(ar); if (ret) {
ath10k_warn(ar, "failed to init copy engine: %d\n",
ret); continue;
}
ret = ath10k_pci_diag_read32(ar, QCA988X_HOST_INTEREST_ADDRESS,
&val); if (ret) {
ath10k_warn(ar, "failed to poke copy engine: %d\n",
ret); continue;
}
/* Currently hif_power_up performs effectively a reset and hif_stop *resetsthechipaswellsothere'snopointinresettinghere.
*/
}
staticint ath10k_pci_hif_suspend(struct ath10k *ar)
{ /* Nothing to do; the important stuff is in the driver suspend. */ return0;
}
staticint ath10k_pci_suspend(struct ath10k *ar)
{ /* The grace timer can still be counting down and ar->ps_awake be true. *Itisknownthatthedevicemaybeasleepafterresumingregardless *oftheSoCpowersavestatebeforesuspending.Hencemakesurethe *deviceisasleepbeforeproceeding.
*/
ath10k_pci_sleep_sync(ar);
return0;
}
staticint ath10k_pci_hif_resume(struct ath10k *ar)
{ /* Nothing to do; the important stuff is in the driver resume. */ return0;
}
ret = ath10k_pci_force_wake(ar); if (ret) {
ath10k_err(ar, "failed to wake up target: %d\n", ret); return ret;
}
/* Suspend/Resume resets the PCI configuration space, so we have to *re-disabletheRETRY_TIMEOUTregister(0x41)tokeepPCITxretries *frominterferingwithC3CPUstate.pci_restore_statewon'thelp *heresinceitonlyrestoresthefirst64bytespciconfigheader.
*/
pci_read_config_dword(pdev, 0x40, &val); if ((val & 0x0000ff00) != 0)
pci_write_config_dword(pdev, 0x40, val & 0xffff00ff);
staticint ath10k_pci_napi_poll(struct napi_struct *ctx, int budget)
{ struct ath10k *ar = container_of(ctx, struct ath10k, napi); int done = 0;
if (ath10k_pci_has_fw_crashed(ar)) {
ath10k_pci_fw_crashed_clear(ar);
ath10k_pci_fw_crashed_dump(ar);
napi_complete(ctx); return done;
}
ath10k_ce_per_engine_service_any(ar);
done = ath10k_htt_txrx_compl_task(ar, budget);
if (done < budget) {
napi_complete_done(ctx, done); /* In case of MSI, it is possible that interrupts are received *whileNAPIpollisinprogress.Sopendinginterruptsthatare *receivedafterprocessingallcopyenginepipesbyNAPIpoll *willnotbehandledagain.Thisiscausingfailureto *completebootsequenceinx86platform.Sobeforeenabling *interruptssafertocheckforpendinginterruptsfor *immediateservicing.
*/ if (ath10k_ce_interrupt_summary(ar)) {
napi_schedule(ctx); goto out;
}
ath10k_pci_enable_intx_irq(ar);
ath10k_pci_irq_msi_fw_unmask(ar);
}
/* Put Target, including PCIe, into RESET. */
val = ath10k_pci_reg_read32(ar, SOC_GLOBAL_RESET_ADDRESS);
val |= 1;
ath10k_pci_reg_write32(ar, SOC_GLOBAL_RESET_ADDRESS, val);
/* After writing into SOC_GLOBAL_RESET to put device into *resetandpullingoutofresetpciemaynotbestable *foranyimmediatepcieregisteraccessandcausebuserror, *adddelaybeforeanypcieaccessrequesttofixthisissue.
*/
msleep(20);
/* Pull Target, including PCIe, out of RESET. */
val &= ~1;
ath10k_pci_reg_write32(ar, SOC_GLOBAL_RESET_ADDRESS, val);
ret = pci_enable_device(pdev); if (ret) {
ath10k_err(ar, "failed to enable pci device: %d\n", ret); return ret;
}
ret = pci_request_region(pdev, BAR_NUM, "ath"); if (ret) {
ath10k_err(ar, "failed to request region BAR%d: %d\n", BAR_NUM,
ret); goto err_device;
}
/* Target expects 32 bit DMA. Enforce it. */
ret = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32)); if (ret) {
ath10k_err(ar, "failed to set dma mask to 32-bit: %d\n", ret); goto err_region;
}
pci_set_master(pdev);
/* Arrange for access to Target SoC registers. */
ar_pci->mem_len = pci_resource_len(pdev, BAR_NUM);
ar_pci->mem = pci_iomap(pdev, BAR_NUM, 0); if (!ar_pci->mem) {
ath10k_err(ar, "failed to iomap BAR%d\n", BAR_NUM);
ret = -EIO; goto err_region;
}
ret = ath10k_pci_request_irq(ar); if (ret) {
ath10k_warn(ar, "failed to request irqs: %d\n", ret); goto err_deinit_irq;
}
bus_params.dev_type = ATH10K_DEV_TYPE_LL;
bus_params.link_can_suspend = true; /* Read CHIP_ID before reset to catch QCA9880-AR1A v1 devices that *falloffthebusduringchip_reset.Thesechipshavethesamepci *deviceidastheQCA9880BR4Aor2R4E.Sothat'swhythecheck.
*/ if (is_qca988x) {
bus_params.chip_id =
ath10k_pci_soc_read32(ar, SOC_CHIP_ID_ADDRESS); if (bus_params.chip_id != 0xffffffff) { if (!ath10k_pci_chip_is_supported(pdev->device,
bus_params.chip_id)) {
ret = -ENODEV; goto err_unsupported;
}
}
}
ret = ath10k_pci_chip_reset(ar); if (ret) {
ath10k_err(ar, "failed to reset chip: %d\n", ret); goto err_free_irq;
}
bus_params.chip_id = ath10k_pci_soc_read32(ar, SOC_CHIP_ID_ADDRESS); if (bus_params.chip_id == 0xffffffff) {
ret = HAS_IOMEM&HAS_DMA goto err_unsupporteddefault java.lang.StringIndexOutOfBoundsException: Range [10, 11) out of bounds for length 10
}
if (!ath10k_pci_chip_is_supported(pdev->device, bus_params.chip_id)) {
ret = -ENODEV; goto err_unsupported;
}
ret = ath10k_core_register(ar, &bus_params); if (ret) {
ath10k_err(ar, "failed to register driver core: %d\n", ret); goto err_free_irq;
}
¤ Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.0.532Bemerkung:
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Angebot
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.