| /* |
| * Copyright (c) 2005-2011 Atheros Communications Inc. |
| * Copyright (c) 2011-2013 Qualcomm Atheros, Inc. |
| * |
| * Permission to use, copy, modify, and/or distribute this software for any |
| * purpose with or without fee is hereby granted, provided that the above |
| * copyright notice and this permission notice appear in all copies. |
| * |
| * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
| * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
| * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR |
| * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
| * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN |
| * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF |
| * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
| */ |
| |
| #ifndef _PCI_H_ |
| #define _PCI_H_ |
| |
| #include <linux/interrupt.h> |
| |
| #include "hw.h" |
| #include "ce.h" |
| |
| /* FW dump area */ |
| #define REG_DUMP_COUNT_QCA988X 60 |
| |
| /* |
| * maximum number of bytes that can be handled atomically by DiagRead/DiagWrite |
| */ |
| #define DIAG_TRANSFER_LIMIT 2048 |
| |
| /* |
| * maximum number of bytes that can be |
| * handled atomically by DiagRead/DiagWrite |
| */ |
| #define DIAG_TRANSFER_LIMIT 2048 |
| |
| struct bmi_xfer { |
| struct completion done; |
| bool wait_for_resp; |
| u32 resp_len; |
| }; |
| |
| struct ath10k_pci_compl { |
| struct list_head list; |
| int send_or_recv; |
| struct ce_state *ce_state; |
| struct hif_ce_pipe_info *pipe_info; |
| void *transfer_context; |
| unsigned int nbytes; |
| unsigned int transfer_id; |
| unsigned int flags; |
| }; |
| |
| /* compl_state.send_or_recv */ |
| #define HIF_CE_COMPLETE_FREE 0 |
| #define HIF_CE_COMPLETE_SEND 1 |
| #define HIF_CE_COMPLETE_RECV 2 |
| |
| /* |
| * PCI-specific Target state |
| * |
| * NOTE: Structure is shared between Host software and Target firmware! |
| * |
| * Much of this may be of interest to the Host so |
| * HOST_INTEREST->hi_interconnect_state points here |
| * (and all members are 32-bit quantities in order to |
| * facilitate Host access). In particular, Host software is |
| * required to initialize pipe_cfg_addr and svc_to_pipe_map. |
| */ |
| struct pcie_state { |
| /* Pipe configuration Target address */ |
| /* NB: ce_pipe_config[CE_COUNT] */ |
| u32 pipe_cfg_addr; |
| |
| /* Service to pipe map Target address */ |
| /* NB: service_to_pipe[PIPE_TO_CE_MAP_CN] */ |
| u32 svc_to_pipe_map; |
| |
| /* number of MSI interrupts requested */ |
| u32 msi_requested; |
| |
| /* number of MSI interrupts granted */ |
| u32 msi_granted; |
| |
| /* Message Signalled Interrupt address */ |
| u32 msi_addr; |
| |
| /* Base data */ |
| u32 msi_data; |
| |
| /* |
| * Data for firmware interrupt; |
| * MSI data for other interrupts are |
| * in various SoC registers |
| */ |
| u32 msi_fw_intr_data; |
| |
| /* PCIE_PWR_METHOD_* */ |
| u32 power_mgmt_method; |
| |
| /* PCIE_CONFIG_FLAG_* */ |
| u32 config_flags; |
| }; |
| |
| /* PCIE_CONFIG_FLAG definitions */ |
| #define PCIE_CONFIG_FLAG_ENABLE_L1 0x0000001 |
| |
| /* Host software's Copy Engine configuration. */ |
| #define CE_ATTR_FLAGS 0 |
| |
| /* |
| * Configuration information for a Copy Engine pipe. |
| * Passed from Host to Target during startup (one per CE). |
| * |
| * NOTE: Structure is shared between Host software and Target firmware! |
| */ |
| struct ce_pipe_config { |
| u32 pipenum; |
| u32 pipedir; |
| u32 nentries; |
| u32 nbytes_max; |
| u32 flags; |
| u32 reserved; |
| }; |
| |
| /* |
| * Directions for interconnect pipe configuration. |
| * These definitions may be used during configuration and are shared |
| * between Host and Target. |
| * |
| * Pipe Directions are relative to the Host, so PIPEDIR_IN means |
| * "coming IN over air through Target to Host" as with a WiFi Rx operation. |
| * Conversely, PIPEDIR_OUT means "going OUT from Host through Target over air" |
| * as with a WiFi Tx operation. This is somewhat awkward for the "middle-man" |
| * Target since things that are "PIPEDIR_OUT" are coming IN to the Target |
| * over the interconnect. |
| */ |
| #define PIPEDIR_NONE 0 |
| #define PIPEDIR_IN 1 /* Target-->Host, WiFi Rx direction */ |
| #define PIPEDIR_OUT 2 /* Host->Target, WiFi Tx direction */ |
| #define PIPEDIR_INOUT 3 /* bidirectional */ |
| |
| /* Establish a mapping between a service/direction and a pipe. */ |
| struct service_to_pipe { |
| u32 service_id; |
| u32 pipedir; |
| u32 pipenum; |
| }; |
| |
| enum ath10k_pci_features { |
| ATH10K_PCI_FEATURE_MSI_X = 0, |
| ATH10K_PCI_FEATURE_HW_1_0_WARKAROUND = 1, |
| |
| /* keep last */ |
| ATH10K_PCI_FEATURE_COUNT |
| }; |
| |
| /* Per-pipe state. */ |
| struct hif_ce_pipe_info { |
| /* Handle of underlying Copy Engine */ |
| struct ce_state *ce_hdl; |
| |
| /* Our pipe number; facilitiates use of pipe_info ptrs. */ |
| u8 pipe_num; |
| |
| /* Convenience back pointer to hif_ce_state. */ |
| struct ath10k *hif_ce_state; |
| |
| size_t buf_sz; |
| |
| /* protects compl_free and num_send_allowed */ |
| spinlock_t pipe_lock; |
| |
| /* List of free CE completion slots */ |
| struct list_head compl_free; |
| |
| /* Limit the number of outstanding send requests. */ |
| int num_sends_allowed; |
| |
| struct ath10k_pci *ar_pci; |
| struct tasklet_struct intr; |
| }; |
| |
| struct ath10k_pci { |
| struct pci_dev *pdev; |
| struct device *dev; |
| struct ath10k *ar; |
| void __iomem *mem; |
| int cacheline_sz; |
| |
| DECLARE_BITMAP(features, ATH10K_PCI_FEATURE_COUNT); |
| |
| /* |
| * Number of MSI interrupts granted, 0 --> using legacy PCI line |
| * interrupts. |
| */ |
| int num_msi_intrs; |
| |
| struct tasklet_struct intr_tq; |
| struct tasklet_struct msi_fw_err; |
| |
| /* Number of Copy Engines supported */ |
| unsigned int ce_count; |
| |
| int started; |
| |
| atomic_t keep_awake_count; |
| bool verified_awake; |
| |
| /* List of CE completions to be processed */ |
| struct list_head compl_process; |
| |
| /* protects compl_processing and compl_process */ |
| spinlock_t compl_lock; |
| |
| bool compl_processing; |
| |
| struct hif_ce_pipe_info pipe_info[CE_COUNT_MAX]; |
| |
| struct ath10k_hif_cb msg_callbacks_current; |
| |
| /* Target address used to signal a pending firmware event */ |
| u32 fw_indicator_address; |
| |
| /* Copy Engine used for Diagnostic Accesses */ |
| struct ce_state *ce_diag; |
| |
| /* FIXME: document what this really protects */ |
| spinlock_t ce_lock; |
| |
| /* Map CE id to ce_state */ |
| struct ce_state *ce_id_to_state[CE_COUNT_MAX]; |
| |
| /* makes sure that dummy reads are atomic */ |
| spinlock_t hw_v1_workaround_lock; |
| }; |
| |
| static inline struct ath10k_pci *ath10k_pci_priv(struct ath10k *ar) |
| { |
| return ar->hif.priv; |
| } |
| |
| static inline u32 ath10k_pci_reg_read32(void __iomem *mem, u32 addr) |
| { |
| return ioread32(mem + PCIE_LOCAL_BASE_ADDRESS + addr); |
| } |
| |
| static inline void ath10k_pci_reg_write32(void __iomem *mem, u32 addr, u32 val) |
| { |
| iowrite32(val, mem + PCIE_LOCAL_BASE_ADDRESS + addr); |
| } |
| |
| #define ATH_PCI_RESET_WAIT_MAX 10 /* ms */ |
| #define PCIE_WAKE_TIMEOUT 5000 /* 5ms */ |
| |
| #define BAR_NUM 0 |
| |
| #define CDC_WAR_MAGIC_STR 0xceef0000 |
| #define CDC_WAR_DATA_CE 4 |
| |
| /* |
| * TODO: Should be a function call specific to each Target-type. |
| * This convoluted macro converts from Target CPU Virtual Address Space to CE |
| * Address Space. As part of this process, we conservatively fetch the current |
| * PCIE_BAR. MOST of the time, this should match the upper bits of PCI space |
| * for this device; but that's not guaranteed. |
| */ |
| #define TARG_CPU_SPACE_TO_CE_SPACE(ar, pci_addr, addr) \ |
| (((ioread32((pci_addr)+(SOC_CORE_BASE_ADDRESS| \ |
| CORE_CTRL_ADDRESS)) & 0x7ff) << 21) | \ |
| 0x100000 | ((addr) & 0xfffff)) |
| |
| /* Wait up to this many Ms for a Diagnostic Access CE operation to complete */ |
| #define DIAG_ACCESS_CE_TIMEOUT_MS 10 |
| |
| /* |
| * This API allows the Host to access Target registers directly |
| * and relatively efficiently over PCIe. |
| * This allows the Host to avoid extra overhead associated with |
| * sending a message to firmware and waiting for a response message |
| * from firmware, as is done on other interconnects. |
| * |
| * Yet there is some complexity with direct accesses because the |
| * Target's power state is not known a priori. The Host must issue |
| * special PCIe reads/writes in order to explicitly wake the Target |
| * and to verify that it is awake and will remain awake. |
| * |
| * Usage: |
| * |
| * Use ath10k_pci_read32 and ath10k_pci_write32 to access Target space. |
| * These calls must be bracketed by ath10k_pci_wake and |
| * ath10k_pci_sleep. A single BEGIN/END pair is adequate for |
| * multiple READ/WRITE operations. |
| * |
| * Use ath10k_pci_wake to put the Target in a state in |
| * which it is legal for the Host to directly access it. This |
| * may involve waking the Target from a low power state, which |
| * may take up to 2Ms! |
| * |
| * Use ath10k_pci_sleep to tell the Target that as far as |
| * this code path is concerned, it no longer needs to remain |
| * directly accessible. BEGIN/END is under a reference counter; |
| * multiple code paths may issue BEGIN/END on a single targid. |
| */ |
| static inline void ath10k_pci_write32(struct ath10k *ar, u32 offset, |
| u32 value) |
| { |
| struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); |
| void __iomem *addr = ar_pci->mem; |
| |
| if (test_bit(ATH10K_PCI_FEATURE_HW_1_0_WARKAROUND, ar_pci->features)) { |
| unsigned long irq_flags; |
| |
| spin_lock_irqsave(&ar_pci->hw_v1_workaround_lock, irq_flags); |
| |
| ioread32(addr+offset+4); /* 3rd read prior to write */ |
| ioread32(addr+offset+4); /* 2nd read prior to write */ |
| ioread32(addr+offset+4); /* 1st read prior to write */ |
| iowrite32(value, addr+offset); |
| |
| spin_unlock_irqrestore(&ar_pci->hw_v1_workaround_lock, |
| irq_flags); |
| } else { |
| iowrite32(value, addr+offset); |
| } |
| } |
| |
| static inline u32 ath10k_pci_read32(struct ath10k *ar, u32 offset) |
| { |
| struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); |
| |
| return ioread32(ar_pci->mem + offset); |
| } |
| |
| extern unsigned int ath10k_target_ps; |
| |
| void ath10k_do_pci_wake(struct ath10k *ar); |
| void ath10k_do_pci_sleep(struct ath10k *ar); |
| |
| static inline void ath10k_pci_wake(struct ath10k *ar) |
| { |
| if (ath10k_target_ps) |
| ath10k_do_pci_wake(ar); |
| } |
| |
| static inline void ath10k_pci_sleep(struct ath10k *ar) |
| { |
| if (ath10k_target_ps) |
| ath10k_do_pci_sleep(ar); |
| } |
| |
| #endif /* _PCI_H_ */ |