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.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.